diff --git a/.gitignore b/.gitignore index d386c3123..efda2ecd4 100644 --- a/.gitignore +++ b/.gitignore @@ -44,3 +44,4 @@ buildNumber.properties *.iml out gen +*DS_Store diff --git a/README.md b/README.md index 8b4f079eb..60b61dd9b 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,10 @@ 本项目为全栈工程师学习笔记,不求最深最好,开源技术搬运工 +https://github.com/GrowingGit/GitHub-Chinese-Top-Charts 开源API + + + 其他项目: Spring Cloud 微服务学习笔记 [https://github.com/bage2014/study-micro-services](https://github.com/bage2014/study-micro-services) @@ -25,7 +29,7 @@ AOP实用技巧;比如打日志等 m3u parser https://github.com/BjoernPetersen/m3u-parser -知识汇总:::: +知识汇总: https://github.com/CyC2018/CS-Notes @@ -49,9 +53,21 @@ https://github.com/jobbole/awesome-java-cn https://mp.weixin.qq.com/s/kro_gyUQrdKSy1x3ETwb5A +限流组件 + +重试组件 + ## Others ## +小工具?? + +https://github.com/jgraph/drawio-desktop/releases/tag/v20.3.0 + + + + + GIT 地址 https://git-scm.com/book/zh/v2/%E8%B5%B7%E6%AD%A5-%E5%AE%89%E8%A3%85-Git diff --git a/README_Template.md b/README_Template.md new file mode 100644 index 000000000..58ca749f5 --- /dev/null +++ b/README_Template.md @@ -0,0 +1,35 @@ +# study- XXX + +文档编写目的 + +当前进度 + +后期规划 + +项目代办 + +## 参考链接 + +官网文档 XXX + +## 快速开始 + +下载运行 + +``` +Publish Remote + +``` + +## 应用实践 + +下载运行 + +``` +Publish Remote + +``` + +## 原理解析 + +基本原理 \ No newline at end of file diff --git a/bage-spring-boot-starter-ping-test/pom.xml b/bage-spring-boot-starter-ping-test/pom.xml index 3304759e8..cbcef443e 100644 --- a/bage-spring-boot-starter-ping-test/pom.xml +++ b/bage-spring-boot-starter-ping-test/pom.xml @@ -15,7 +15,9 @@ http://www.example.com - UTF-8 + 1.8 + 1.8 + 2.0.1.RELEASE diff --git a/bage-spring-boot-starter-ping/README.md b/bage-spring-boot-starter-ping/README.md index 0f06dc689..e92d4f7ac 100644 --- a/bage-spring-boot-starter-ping/README.md +++ b/bage-spring-boot-starter-ping/README.md @@ -7,6 +7,8 @@ ``` UTF-8 + 1.8 + 1.8 2.0.1.RELEASE diff --git a/bage-spring-boot-starter-ping/pom.xml b/bage-spring-boot-starter-ping/pom.xml index 231aa7e42..45e6d816e 100644 --- a/bage-spring-boot-starter-ping/pom.xml +++ b/bage-spring-boot-starter-ping/pom.xml @@ -15,7 +15,9 @@ http://www.example.com - UTF-8 + 1.8 + 1.8 + 2.0.1.RELEASE diff --git a/pom.xml b/pom.xml index f4978018f..f38051e52 100644 --- a/pom.xml +++ b/pom.xml @@ -16,6 +16,8 @@ UTF-8 + 1.8 + 1.8 study-spring-boot @@ -51,6 +53,7 @@ study-gateway study-sql study-quartz + study-guava study-shiro-web-tutorial study-rabbitmq study-tomcat @@ -135,7 +138,9 @@ study-summary study-id-generator study-stream-m3u8 + study-hystrix study-resilience4j study-ews-java-api @@ -144,7 +149,27 @@ study-sftp study-jmockit study-mockito + + study-file-online-preview + study-jol + study-servlet + study-lombok + study-spring-boot-elk + study-mapstruct + study-sentinel + study-mybatis-plus + study-oceanbase + study-spring-boot-trace + study-spring-boot-trace-server + study-spring-boot-wavefront + study-spring-boot-valid + study-spring-boot-web-socket + study-spring-cloud-circuit-breaker + study-crawler + study-jenkins + study-nginx \ No newline at end of file diff --git a/study-activemq/pom.xml b/study-activemq/pom.xml index b9c5f4a01..56365c9bc 100644 --- a/study-activemq/pom.xml +++ b/study-activemq/pom.xml @@ -11,7 +11,9 @@ study-activemq http://maven.apache.org - UTF-8 + + 1.8 + 1.8 diff --git a/study-algorithm/pom.xml b/study-algorithm/pom.xml index 727d0b784..5e77e61e9 100644 --- a/study-algorithm/pom.xml +++ b/study-algorithm/pom.xml @@ -11,7 +11,9 @@ study-algorithm http://maven.apache.org - UTF-8 + + 1.8 + 1.8 diff --git a/study-algorithm/src/main/java/com/bage/study/algorithm/leetcode/MinDeepTree.java b/study-algorithm/src/main/java/com/bage/study/algorithm/leetcode/MinDeepTree.java new file mode 100644 index 000000000..040f92cb5 --- /dev/null +++ b/study-algorithm/src/main/java/com/bage/study/algorithm/leetcode/MinDeepTree.java @@ -0,0 +1,24 @@ +package com.bage.study.algorithm.leetcode; + +import java.util.Objects; + + +/** + * https://leetcode-cn.com/problems/minimum-depth-of-binary-tree/ + * // todo ddd + */ +public class MinDeepTree { + public ListNode reverseList(ListNode head) { + if(Objects.isNull(head) || Objects.isNull(head.next)){ + return head; + } + ListNode listNode = reverseList(head.next); + head.next.next = head; + head.next = null; + return listNode; + } +} + +class TreeNode { + TreeNode next; + } diff --git a/study-algorithm/src/main/java/com/bage/study/algorithm/leetcode/RevertNodes.java b/study-algorithm/src/main/java/com/bage/study/algorithm/leetcode/RevertNodes.java new file mode 100644 index 000000000..8b11f3a5e --- /dev/null +++ b/study-algorithm/src/main/java/com/bage/study/algorithm/leetcode/RevertNodes.java @@ -0,0 +1,39 @@ +package com.bage.study.algorithm.leetcode; + +import java.util.Objects; + +/** + * https://leetcode-cn.com/problems/UHnkqh/ + */ +public class RevertNodes { +} + +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +class Solution { + public ListNode reverseList(ListNode head) { + if(Objects.isNull(head) || Objects.isNull(head.next)){ + return head; + } + ListNode listNode = reverseList(head.next); + head.next.next = head; + head.next = null; + return listNode; + } +} + + class ListNode { + int val; + ListNode next; + ListNode() {} + ListNode(int val) { this.val = val; } + ListNode(int val, ListNode next) { this.val = val; this.next = next; } + } diff --git a/study-algorithm/src/main/java/com/bage/study/algorithm/leetcode/SortedArraySum.java b/study-algorithm/src/main/java/com/bage/study/algorithm/leetcode/SortedArraySum.java new file mode 100644 index 000000000..7ea6c3c56 --- /dev/null +++ b/study-algorithm/src/main/java/com/bage/study/algorithm/leetcode/SortedArraySum.java @@ -0,0 +1,19 @@ +package com.bage.study.algorithm.leetcode; + +import java.util.Objects; + +/** + * https://leetcode-cn.com/problems/kLl5u1/ + * SortedArraySum + */ +public class SortedArraySum { + public ListNode reverseList(ListNode head) { + if(Objects.isNull(head) || Objects.isNull(head.next)){ + return head; + } + ListNode listNode = reverseList(head.next); + head.next.next = head; + head.next = null; + return listNode; + } +} diff --git a/study-app-tutorials/.gitignore b/study-app-tutorials/.gitignore new file mode 100644 index 000000000..0fa6b675c --- /dev/null +++ b/study-app-tutorials/.gitignore @@ -0,0 +1,46 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Web related +lib/generated_plugin_registrant.dart + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/study-app-tutorials/.metadata b/study-app-tutorials/.metadata new file mode 100644 index 000000000..a5584fc37 --- /dev/null +++ b/study-app-tutorials/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 18116933e77adc82f80866c928266a5b4f1ed645 + channel: stable + +project_type: app diff --git a/study-app-tutorials/README.md b/study-app-tutorials/README.md new file mode 100644 index 000000000..200cf352e --- /dev/null +++ b/study-app-tutorials/README.md @@ -0,0 +1,169 @@ +# tutorials + +A tutorials project. + +## todo +登陆实效、1个月 + +个人名片 +商用名片 +家用名片 + +标签管理 +名片管理 + + +个人经典事迹?? 别人点赞,验证真伪!! + +我的音乐?视频??啥的?? + +自定义名片 + +文件上传 + +Mock 服务,,比如 10% 出现404 + +名片页面设计 + + + +2022年结束---上线APP + + + +## 参考资料 + +https://javiercbk.github.io/json_to_dart/ +https://jsontodart.com/ + +Flutter实战 https://book.flutterchina.club/ + +https://flutter.cn/ + +https://docs.flutter.dev/development/ui/widgets/material + +带源码【组件 + 源码】 +很多很赞 +https://github.com/bukunmialuko/flutter_ui_kit_obkm + +很多很赞 +https://github.com/anoochit/uikits + +很多很赞 +https://github.com/lohanidamodar/flutter_ui_challenges + +常用页面 +https://github.com/simplezhli/flutter_deer + +应用集合 +https://github.com/Vignesh0404/Flutter-UI-Kit + +https://github.com/vinothvino42/SwiggyUI + +https://github.com/usman18/Flutter-UI-Kit + + + +官网常用 +https://docs.flutter.dev/development/ui/widgets/material + +https://doc.flutterchina.club/widgets/ + + + +页面合集 + + +https://fluttertemplates.dev/ + +https://github.com/iampawan/Flutter-UI-Kit + +https://github.com/olayemii/flutter-ui-kits + +https://www.behance.net/gallery/69411833/Backpack-UI-Kit-Free-for-Adobe-XD UI资源 很多 + + + +闹钟、通知类 + +https://pub.flutter-io.cn/packages/flutter_local_notifications + +https://pub.flutter-io.cn/packages/awesome_notifications +https://pub.dev/packages/android_alarm_manager_plus + + +Flutter UI + +https://pub.flutter-io.cn/packages/fluent_ui/example +https://bdlukaa.github.io/fluent_ui/ + + + + +FLutter Icon + +https://www.fluttericon.cn/ + + +生成签名 +```csharp +keytool -genkey -v -keystore ./sign.jks -keyalg RSA -keysize 2048 -validity 10000 -alias sign +``` + +输入密码 +bage.app + +创建文件 +key.properties +```csharp +storePassword=bage.app +keyPassword=bage.app +keyAlias=sign +storeFile=sign.jks +``` + +build.gradle +```csharp + +def keystorePropertiesFile = rootProject.file("app/key/key.properties") +def keystoreProperties = new Properties() +keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) + + +android 节点下: + + signingConfigs { + release { + keyAlias 'sign' + keyPassword 'bage.app' + storeFile file('key/sign.jks') + storePassword 'android' + } + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig signingConfigs.release + // signingConfig signingConfigs.debug + + } + } + +``` + + + +后端地址 + +10.0.2.2 + + + +账号: + +bage2014@qq.com + +123456 + diff --git a/study-app-tutorials/analysis_options.yaml b/study-app-tutorials/analysis_options.yaml new file mode 100644 index 000000000..61b6c4de1 --- /dev/null +++ b/study-app-tutorials/analysis_options.yaml @@ -0,0 +1,29 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at + # https://dart-lang.github.io/linter/lints/index.html. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/study-app-tutorials/android/.gitignore b/study-app-tutorials/android/.gitignore new file mode 100644 index 000000000..2d60e8488 --- /dev/null +++ b/study-app-tutorials/android/.gitignore @@ -0,0 +1,13 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +app/key/key.properties +**/*.keystore +**/*.jks diff --git a/study-app-tutorials/android/app/build.gradle b/study-app-tutorials/android/app/build.gradle new file mode 100644 index 000000000..4a26158c4 --- /dev/null +++ b/study-app-tutorials/android/app/build.gradle @@ -0,0 +1,72 @@ + +def keystorePropertiesFile = rootProject.file("app/key/key.properties") +def keystoreProperties = new Properties() +keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) + + +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterRoot = localProperties.getProperty('flutter.sdk') +if (flutterRoot == null) { + throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +apply plugin: 'com.android.application' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + compileSdkVersion 31 + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId "com.bage.tutorials" + minSdkVersion 16 + targetSdkVersion 31 + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + } + + signingConfigs { + release { + keyAlias 'sign' + keyPassword 'bage.app' + storeFile file('key/sign.jks') + storePassword 'bage.app' + } + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig signingConfigs.release + // signingConfig signingConfigs.debug + + } + } +} + +flutter { + source '../..' +} diff --git a/study-app-tutorials/android/app/src/debug/AndroidManifest.xml b/study-app-tutorials/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 000000000..31b30612d --- /dev/null +++ b/study-app-tutorials/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/study-app-tutorials/android/app/src/main/AndroidManifest.xml b/study-app-tutorials/android/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000..f21ed8597 --- /dev/null +++ b/study-app-tutorials/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/study-app-tutorials/android/app/src/main/java/com/bage/tutorials/MainActivity.java b/study-app-tutorials/android/app/src/main/java/com/bage/tutorials/MainActivity.java new file mode 100644 index 000000000..5fb42eb3d --- /dev/null +++ b/study-app-tutorials/android/app/src/main/java/com/bage/tutorials/MainActivity.java @@ -0,0 +1,6 @@ +package com.bage.tutorials; + +import io.flutter.embedding.android.FlutterActivity; + +public class MainActivity extends FlutterActivity { +} diff --git a/study-app-tutorials/android/app/src/main/res/drawable-v21/launch_background.xml b/study-app-tutorials/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 000000000..f74085f3f --- /dev/null +++ b/study-app-tutorials/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/study-app-tutorials/android/app/src/main/res/drawable/launch_background.xml b/study-app-tutorials/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 000000000..304732f88 --- /dev/null +++ b/study-app-tutorials/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/study-app-tutorials/android/app/src/main/res/drawable/logo128.png b/study-app-tutorials/android/app/src/main/res/drawable/logo128.png new file mode 100644 index 000000000..bfb3553d2 Binary files /dev/null and b/study-app-tutorials/android/app/src/main/res/drawable/logo128.png differ diff --git a/study-app-tutorials/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/study-app-tutorials/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..db77bb4b7 Binary files /dev/null and b/study-app-tutorials/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/study-app-tutorials/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/study-app-tutorials/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..17987b79b Binary files /dev/null and b/study-app-tutorials/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/study-app-tutorials/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/study-app-tutorials/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..09d439148 Binary files /dev/null and b/study-app-tutorials/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/study-app-tutorials/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/study-app-tutorials/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..d5f1c8d34 Binary files /dev/null and b/study-app-tutorials/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/study-app-tutorials/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/study-app-tutorials/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..4d6372eeb Binary files /dev/null and b/study-app-tutorials/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/study-app-tutorials/android/app/src/main/res/values-night/styles.xml b/study-app-tutorials/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 000000000..449a9f930 --- /dev/null +++ b/study-app-tutorials/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/study-app-tutorials/android/app/src/main/res/values/styles.xml b/study-app-tutorials/android/app/src/main/res/values/styles.xml new file mode 100644 index 000000000..d74aa35c2 --- /dev/null +++ b/study-app-tutorials/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/study-app-tutorials/android/app/src/profile/AndroidManifest.xml b/study-app-tutorials/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 000000000..31b30612d --- /dev/null +++ b/study-app-tutorials/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/study-app-tutorials/android/build.gradle b/study-app-tutorials/android/build.gradle new file mode 100644 index 000000000..d063075b6 --- /dev/null +++ b/study-app-tutorials/android/build.gradle @@ -0,0 +1,35 @@ +buildscript { + repositories { + maven { url 'https://maven.aliyun.com/repository/google' } + maven { url 'https://maven.aliyun.com/repository/jcenter' } + maven { url 'http://maven.aliyun.com/nexus/content/groups/public' } + +// google() +// mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:4.1.0' + } +} + +allprojects { + repositories { + maven { url 'https://maven.aliyun.com/repository/google' } + maven { url 'https://maven.aliyun.com/repository/jcenter' } + maven { url 'http://maven.aliyun.com/nexus/content/groups/public' } + +// google() +// mavenCentral() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" + project.evaluationDependsOn(':app') +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/study-app-tutorials/android/gradle.properties b/study-app-tutorials/android/gradle.properties new file mode 100644 index 000000000..94adc3a3f --- /dev/null +++ b/study-app-tutorials/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true +android.enableJetifier=true diff --git a/study-app-tutorials/android/gradle/wrapper/gradle-wrapper.properties b/study-app-tutorials/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..bc6a58afd --- /dev/null +++ b/study-app-tutorials/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri Jun 23 08:50:38 CEST 2017 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip diff --git a/study-app-tutorials/android/settings.gradle b/study-app-tutorials/android/settings.gradle new file mode 100644 index 000000000..44e62bcf0 --- /dev/null +++ b/study-app-tutorials/android/settings.gradle @@ -0,0 +1,11 @@ +include ':app' + +def localPropertiesFile = new File(rootProject.projectDir, "local.properties") +def properties = new Properties() + +assert localPropertiesFile.exists() +localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } + +def flutterSdkPath = properties.getProperty("flutter.sdk") +assert flutterSdkPath != null, "flutter.sdk not set in local.properties" +apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/study-app-tutorials/apks/app-release.apk b/study-app-tutorials/apks/app-release.apk new file mode 100644 index 000000000..2ec5e9fee Binary files /dev/null and b/study-app-tutorials/apks/app-release.apk differ diff --git a/study-app-tutorials/assets/images/logo128.png b/study-app-tutorials/assets/images/logo128.png new file mode 100644 index 000000000..bfb3553d2 Binary files /dev/null and b/study-app-tutorials/assets/images/logo128.png differ diff --git a/study-app-tutorials/assets/images/school-logo.png b/study-app-tutorials/assets/images/school-logo.png new file mode 100644 index 000000000..0e5b92245 Binary files /dev/null and b/study-app-tutorials/assets/images/school-logo.png differ diff --git a/study-app-tutorials/assets/images/user_null.png b/study-app-tutorials/assets/images/user_null.png new file mode 100644 index 000000000..7e620088d Binary files /dev/null and b/study-app-tutorials/assets/images/user_null.png differ diff --git a/study-app-tutorials/assets/locale/i18n_en.json b/study-app-tutorials/assets/locale/i18n_en.json new file mode 100644 index 000000000..e81342527 --- /dev/null +++ b/study-app-tutorials/assets/locale/i18n_en.json @@ -0,0 +1,121 @@ +{ + "all.app.name":"Tutorials ", + "all.list.view.no.data":"No data", + "all.dialog.info.title":"Info", + "all.no.network.tips":"Please make sure connect network", + "all.delete.confirm":"Confirm to delete?", + "all.save.success":"Success", + "all.save.failure":"Failure", + + "main.title":"Welcome ", + + "login.title":"Login ", + "login.account.hint":"Mail ", + "login.password.hint":"Password ", + "login.security.hint":"Security Code ", + "login.button":"Go ", + "login.password.reset":"Forget Password ", + "login.register":"Create New Account ", + "login.success.toast":"Login Success ", + "login.validation.username":"Username can not be hull or empty ", + "login.validation.password":"Password can not be hull or empty ", + "login.validation.security":"Security code can not be hull or empty ", + + "forget.password.title":"Forget Password ", + "forget.password.mail.hint":"Mail ", + "forget.password.security.code.hint":"Security Code ", + "forget.password.go":"Next ", + "forget.password.security.code.send.hint":"Security Code already Send To ", + "forget.password.reset.confirm":"Reset ", + "forget.password.reset.to.login.hint":"Mail Get Password To Login Again", + "forget.password.reset.to.login":"Login Again ", + + + "register.hint":"Register ", + "register.mail.hint":"Mail ", + "register.password.hint":"Password ", + "register.password.again.hint":"Password Again ", + "register.security.hint":"Security Code ", + "register.next":"Next ", + "register.finish.title":"Register Success ", + "register.finish.hint":"Login To Get It ", + "register.finish.login":"Login ", + "register.security.code.send.hint":"Security Code already Send To ", + "register.go":"Register ", + + + "home.drawer.title":"Tutorials ", + "home.drawer.settings":"Settings ", + "home.drawer.about":"About ", + "home.drawer.exit":"Exit ", + "home.drawer.logout":"Logout ", + "home.drawer.profile":"Profile ", + "home.back.confirm":"Confirm to exit?", + "home.logout.confirm":"Confirm to logout?", + "home.back.exit.double.tips":"Double click to exit", + "home.menu.tv":"TV", + "home.menu.family":"Family", + "home.menu.profile":"Profile", + "home.menu.apks":"Apks", + + "settings.title":"Settings ", + "settings.checkForUpdate":"Check For Update", + "settings.feedbacks":"Feedbacks", + "settings.dir.setting":"Download Directory", + "settings.lang.setting":"System Language", + "settings.name.card":"Name Card", + "settings.download.dir.setting.success":"Download Directory Updated", + "settings.alreadyLatestVersion":"This is already the latest version! ", + "settings.downloading":"Downloading...", + "settings.upgrade.dialog.title":"Do upgrade to the latest version? ", + "settings.upgrade.cancel":"Cancel downloading? ", + "settings.upgrade.open.by.browser":"Oepn with browser ", + "settings.upgrade.open.by.app":"Download in App", + "settings.upgrade.open.cancel":"Cancel ", + "settings.lang.zh":"zh-CN ", + "settings.lang.en":"en-US ", + "settings.lang.cancel":"Cancel ", + + "settings.devTool.title":"Developer Tool", + + "settings.feedback.input.hint":"Feedback", + "settings.feedback.all":"All", + "settings.feedback.my":"My", + + "tv.list.title":"TV List", + "tv.list.bottomAll":"All", + "tv.list.bottomFavorite":"Favorite", + + "about.title":"About", + "about.versions":"Versions", + "about.device.id":"Device ID", + "about.author":"About Author", + "about.share":"Share APP", + + "about.version.start":"Start", + "about.version.name":"Version", + + "about.author.title":"About Author", + "about.author.name":"Name", + + "profile.title":"Me", + + "env.title":"Environment Setting", + "env.dev":"Development", + "env.test":"Test", + "env.prod":"Production", + + "env.edit.title":"Environment Edit", + "env.edit.protocol":"Protocol", + "env.edit.host":"Host", + "env.edit.port":"Port", + + "component.http.no.network":"check network state please", + "component.crop.title":"Crop", + + + "name.card.title":"Name Card", + + + "unknown": "" +} \ No newline at end of file diff --git a/study-app-tutorials/assets/locale/i18n_zh.json b/study-app-tutorials/assets/locale/i18n_zh.json new file mode 100644 index 000000000..c8d7ed525 --- /dev/null +++ b/study-app-tutorials/assets/locale/i18n_zh.json @@ -0,0 +1,118 @@ +{ + "all.app.name":"小陆 ", + "all.list.view.no.data":"暂无数据", + "all.dialog.info.title":"提示", + "all.no.network.tips":"请先连接网络", + "all.delete.confirm":"确认删除?", + "all.save.success":"保存成功", + "all.save.failure":"保存失败", + + "main.title":"欢迎使用 ", + + "login.title":"用户登陆 ", + "login.account.hint":"请输入账号 ", + "login.password.hint":"请输入密码 ", + "login.security.hint":"请输入验证码 ", + "login.button":"立即登陆 ", + "login.password.reset":"忘记密码 ", + "login.register":"创建账号 ", + "login.success.toast":"登陆成功 ", + "login.validation.username":"用户名不能为空 ", + "login.validation.password":"密码不能为空 ", + "login.validation.security":"验证码不能为空 ", + + "forget.password.title":"密码重置 ", + "forget.password.mail.hint":"请输入邮箱 ", + "forget.password.security.code.hint":"请出入验证码 ", + "forget.password.go":"下一步 ", + "forget.password.security.code.send.hint":"验证码已发送到 ", + "forget.password.reset.confirm":"确认重置 ", + "forget.password.reset.finish.hint":"重置成功 ", + "forget.password.reset.to.login.hint":"请查看邮箱获取最新密码,重新登陆 ", + "forget.password.reset.to.login":"重新登陆 ", + + "register.hint":"用户注册 ", + "register.mail.hint":"请输入邮箱 ", + "register.password.hint":"请输入密码 ", + "register.password.again.hint":"请再次输入密码 ", + "register.security.hint":"请输入验证码 ", + "register.next":"下一步 ", + "register.finish.title":"注册成功 ", + "register.finish.hint":"点击登陆,开启新篇章吧 ", + "register.finish.login":"登陆 ", + "register.security.code.send.hint":"验证码已发送到 ", + "register.go":"立即注册 ", + + "home.drawer.title":"小陆 ", + "home.drawer.settings":"设置 ", + "home.drawer.about":"关于 ", + "home.drawer.exit":"退出 ", + "home.drawer.logout":"注销 ", + "home.drawer.profile":"我的 ", + "home.back.confirm":"你确定要退出吗?", + "home.logout.confirm":"你确定要注销吗?", + "home.back.exit.double.tips":"再点击一次退出应用", + "home.menu.tv":"电视", + "home.menu.family":"家族", + "home.menu.profile":"我的", + "home.menu.apks":"应用", + + "settings.title":"设置 ", + "settings.checkForUpdate":"检查更新", + "settings.feedbacks":"用户留言", + "settings.dir.setting":"下载目录", + "settings.lang.setting":"系统语言", + "settings.name.card":"我的名片", + "settings.download.dir.setting.success":"下载目录更新成功", + "settings.alreadyLatestVersion":"当前版本已经是最新版本!", + "settings.downloading":"正在下载...", + "settings.upgrade.dialog.title":"确认更新? ", + "settings.upgrade.cancel":"取消下载? ", + "settings.upgrade.open.by.browser":"浏览器打开 ", + "settings.upgrade.open.by.app":"应用内下载", + "settings.upgrade.open.cancel":"取消 ", + "settings.lang.zh":"中文 ", + "settings.lang.en":"英文 ", + "settings.lang.cancel":"取消 ", + + "settings.devTool.title":"开发者工具", + + "settings.feedback.input.hint":"请输入留言内容", + "settings.feedback.all":"所有留言", + "settings.feedback.my":"我的留言", + + "tv.list.title":"电视列表", + "tv.list.bottomAll":"全部", + "tv.list.bottomFavorite":"喜欢", + + "about.title":"关于", + "about.versions":"版本历史", + "about.device.id":"设备编号", + "about.author":"关于作者", + "about.share":"分享应用", + + "about.version.start":"开始", + "about.version.name":"版本", + + "about.author.title":"关于作者", + "about.author.name":"名字", + + "profile.title":"我的", + + "env.title":"环境设置", + "env.dev":"开发环境", + "env.test":"测试环境", + "env.prod":"生产环境", + + "env.edit.title":"环境编辑", + "env.edit.protocol":"协议", + "env.edit.host":"域名", + "env.edit.port":"端口", + + "component.http.no.network":"请检查网络是否已经连接", + "component.crop.title":"裁剪", + + "name.card.title":"我的名片", + + "unknown": "" +} \ No newline at end of file diff --git a/study-app-tutorials/ios/.gitignore b/study-app-tutorials/ios/.gitignore new file mode 100644 index 000000000..7a7f9873a --- /dev/null +++ b/study-app-tutorials/ios/.gitignore @@ -0,0 +1,34 @@ +**/dgph +*.mode1v3 +*.mode2v3 +*.moved-aside +*.pbxuser +*.perspectivev3 +**/*sync/ +.sconsign.dblite +.tags* +**/.vagrant/ +**/DerivedData/ +Icon? +**/Pods/ +**/.symlinks/ +profile +xcuserdata +**/.generated/ +Flutter/App.framework +Flutter/Flutter.framework +Flutter/Flutter.podspec +Flutter/Generated.xcconfig +Flutter/ephemeral/ +Flutter/app.flx +Flutter/app.zip +Flutter/flutter_assets/ +Flutter/flutter_export_environment.sh +ServiceDefinitions.json +Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!default.mode1v3 +!default.mode2v3 +!default.pbxuser +!default.perspectivev3 diff --git a/study-app-tutorials/ios/Flutter/AppFrameworkInfo.plist b/study-app-tutorials/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 000000000..8d4492f97 --- /dev/null +++ b/study-app-tutorials/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 9.0 + + diff --git a/study-app-tutorials/ios/Flutter/Debug.xcconfig b/study-app-tutorials/ios/Flutter/Debug.xcconfig new file mode 100644 index 000000000..592ceee85 --- /dev/null +++ b/study-app-tutorials/ios/Flutter/Debug.xcconfig @@ -0,0 +1 @@ +#include "Generated.xcconfig" diff --git a/study-app-tutorials/ios/Flutter/Release.xcconfig b/study-app-tutorials/ios/Flutter/Release.xcconfig new file mode 100644 index 000000000..592ceee85 --- /dev/null +++ b/study-app-tutorials/ios/Flutter/Release.xcconfig @@ -0,0 +1 @@ +#include "Generated.xcconfig" diff --git a/study-app-tutorials/ios/Runner.xcodeproj/project.pbxproj b/study-app-tutorials/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 000000000..7ce3e75c8 --- /dev/null +++ b/study-app-tutorials/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,471 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + ); + path = Runner; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1020; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.bage.tutorials; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.bage.tutorials; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.bage.tutorials; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/study-app-tutorials/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/study-app-tutorials/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..919434a62 --- /dev/null +++ b/study-app-tutorials/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/study-app-tutorials/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/study-app-tutorials/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/study-app-tutorials/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/study-app-tutorials/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/study-app-tutorials/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 000000000..f9b0d7c5e --- /dev/null +++ b/study-app-tutorials/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/study-app-tutorials/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/study-app-tutorials/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 000000000..a28140cfd --- /dev/null +++ b/study-app-tutorials/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/study-app-tutorials/ios/Runner.xcworkspace/contents.xcworkspacedata b/study-app-tutorials/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..1d526a16e --- /dev/null +++ b/study-app-tutorials/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/study-app-tutorials/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/study-app-tutorials/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/study-app-tutorials/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/study-app-tutorials/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/study-app-tutorials/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 000000000..f9b0d7c5e --- /dev/null +++ b/study-app-tutorials/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/study-app-tutorials/ios/Runner/AppDelegate.swift b/study-app-tutorials/ios/Runner/AppDelegate.swift new file mode 100644 index 000000000..70693e4a8 --- /dev/null +++ b/study-app-tutorials/ios/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import UIKit +import Flutter + +@UIApplicationMain +@objc class AppDelegate: FlutterAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + GeneratedPluginRegistrant.register(with: self) + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } +} diff --git a/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000..d36b1fab2 --- /dev/null +++ b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,122 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "Icon-App-1024x1024@1x.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 000000000..dc9ada472 Binary files /dev/null and b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ diff --git a/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 000000000..28c6bf030 Binary files /dev/null and b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ diff --git a/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 000000000..2ccbfd967 Binary files /dev/null and b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ diff --git a/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 000000000..f091b6b0b Binary files /dev/null and b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ diff --git a/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 000000000..4cde12118 Binary files /dev/null and b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ diff --git a/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 000000000..d0ef06e7e Binary files /dev/null and b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ diff --git a/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png new file mode 100644 index 000000000..dcdc2306c Binary files /dev/null and b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ diff --git a/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png new file mode 100644 index 000000000..2ccbfd967 Binary files /dev/null and b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ diff --git a/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 000000000..c8f9ed8f5 Binary files /dev/null and b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ diff --git a/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 000000000..a6d6b8609 Binary files /dev/null and b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ diff --git a/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 000000000..a6d6b8609 Binary files /dev/null and b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ diff --git a/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 000000000..75b2d164a Binary files /dev/null and b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ diff --git a/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 000000000..c4df70d39 Binary files /dev/null and b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ diff --git a/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png new file mode 100644 index 000000000..6a84f41e1 Binary files /dev/null and b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ diff --git a/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 000000000..d0e1f5853 Binary files /dev/null and b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ diff --git a/study-app-tutorials/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/study-app-tutorials/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 000000000..0bedcf2fd --- /dev/null +++ b/study-app-tutorials/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "LaunchImage.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/study-app-tutorials/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/study-app-tutorials/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png new file mode 100644 index 000000000..9da19eaca Binary files /dev/null and b/study-app-tutorials/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ diff --git a/study-app-tutorials/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/study-app-tutorials/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 000000000..9da19eaca Binary files /dev/null and b/study-app-tutorials/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ diff --git a/study-app-tutorials/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/study-app-tutorials/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 000000000..9da19eaca Binary files /dev/null and b/study-app-tutorials/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ diff --git a/study-app-tutorials/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/study-app-tutorials/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 000000000..89c2725b7 --- /dev/null +++ b/study-app-tutorials/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + +You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/study-app-tutorials/ios/Runner/Base.lproj/LaunchScreen.storyboard b/study-app-tutorials/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 000000000..f2e259c7c --- /dev/null +++ b/study-app-tutorials/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/study-app-tutorials/ios/Runner/Base.lproj/Main.storyboard b/study-app-tutorials/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 000000000..f3c28516f --- /dev/null +++ b/study-app-tutorials/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/study-app-tutorials/ios/Runner/Info.plist b/study-app-tutorials/ios/Runner/Info.plist new file mode 100644 index 000000000..73401620b --- /dev/null +++ b/study-app-tutorials/ios/Runner/Info.plist @@ -0,0 +1,45 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + tutorials + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + + diff --git a/study-app-tutorials/ios/Runner/Runner-Bridging-Header.h b/study-app-tutorials/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 000000000..308a2a560 --- /dev/null +++ b/study-app-tutorials/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/study-app-tutorials/lib/component/cache/http_request_caches.dart b/study-app-tutorials/lib/component/cache/http_request_caches.dart new file mode 100644 index 000000000..ab26e5e36 --- /dev/null +++ b/study-app-tutorials/lib/component/cache/http_request_caches.dart @@ -0,0 +1,88 @@ +import 'package:tutorials/component/sp/shared_preference_helper.dart'; +import 'package:tutorials/constant/sp_constant.dart'; +import 'package:tutorials/model/request_host.dart'; + +class HttpRequestCaches { + static List caches = [ + RequestHost(), + RequestHost(), + RequestHost(), + RequestHost() + ]; + static String _index = "1"; + + static void inits() async { + init("1"); + init("2"); + init("3"); + _index = await SharedPreferenceHelper.get(SpConstant.environment_index, "1"); + } + + static void init(String index) async { + String _protocol = await SharedPreferenceHelper.get( + _buildKey(SpConstant.protocol_key, index), + SpConstant.protocol_value_default); + String _host = await SharedPreferenceHelper.get( + _buildKey(SpConstant.host_key, index), SpConstant.host_value_default); + String _port = await SharedPreferenceHelper.get( + _buildKey(SpConstant.port_key, index), SpConstant.port_value_default); + caches[int.parse(index)].protocol = _protocol; + caches[int.parse(index)].host = _host; + caches[int.parse(index)].port = _port; + } + + static Future setProtocol(String value, String index) async { + caches[int.parse(index)].protocol = value; + return SharedPreferenceHelper.set( + _buildKey(SpConstant.protocol_key, index), value); + } + + static Future setHost(String value, String index) async { + caches[int.parse(index)].host = value; + return SharedPreferenceHelper.set( + _buildKey(SpConstant.host_key, index), value); + } + + static Future setPort(String value, String index) async { + caches[int.parse(index)].port = value; + return SharedPreferenceHelper.set( + _buildKey(SpConstant.port_key, index), value); + } + + static Future setIndex(String index) async{ + _index = index; + return SharedPreferenceHelper.set(SpConstant.environment_index, index); + } + + static String getIndex() { + return _index; + } + + static String getProtocol() { + return caches[int.parse(_index)].protocol; + } + + static String getIndexProtocol(String index) { + return caches[int.parse(index)].protocol; + } + + static String getHost() { + return caches[int.parse(_index)].host; + } + + static String getIndexHost(String index) { + return caches[int.parse(index)].host; + } + + static String getPort() { + return caches[int.parse(_index)].port; + } + + static String getIndexPort(String index) { + return caches[int.parse(index)].port; + } + + static String _buildKey(String key, String index) { + return "http_request_" + index + "_" + key; + } +} diff --git a/study-app-tutorials/lib/component/cache/setting_caches.dart b/study-app-tutorials/lib/component/cache/setting_caches.dart new file mode 100644 index 000000000..72529a17a --- /dev/null +++ b/study-app-tutorials/lib/component/cache/setting_caches.dart @@ -0,0 +1,24 @@ +import 'package:tutorials/component/sp/shared_preference_helper.dart'; +import 'package:tutorials/constant/sp_constant.dart'; + +class SettingCaches { + static Future getDownloadDirectory() { + return SharedPreferenceHelper.get(SpConstant.download_dir_cache_key, + SpConstant.download_dir_value_default); + } + + static Future cacheDownloadDirectory(String value) { + return SharedPreferenceHelper.set(SpConstant.download_dir_cache_key, value); + } + + + static Future getMockSwitch() { + return SharedPreferenceHelper.get(SpConstant.mock_switch_cache_key, + SpConstant.mock_switch_value_default); + } + + + static Future cacheMockSwitch(String value) { + return SharedPreferenceHelper.set(SpConstant.mock_switch_cache_key, value); + } +} diff --git a/study-app-tutorials/lib/component/cache/token_caches.dart b/study-app-tutorials/lib/component/cache/token_caches.dart new file mode 100644 index 000000000..db662d36b --- /dev/null +++ b/study-app-tutorials/lib/component/cache/token_caches.dart @@ -0,0 +1,31 @@ + +import 'package:tutorials/component/sp/shared_preference_helper.dart'; +import 'package:tutorials/constant/sp_constant.dart'; + +class TokenCaches{ + + static Future getAccessToken(){ + return SharedPreferenceHelper.get(SpConstant.token_access_key,''); + } + + static Future cacheAccessToken(String accessToken){ + return SharedPreferenceHelper.set(SpConstant.token_access_key, accessToken); + } + + static Future clearAccessToken(){ + return SharedPreferenceHelper.set(SpConstant.token_access_key, ''); + } + + static Future getRefreshToken(){ + return SharedPreferenceHelper.get(SpConstant.token_refresh_key,''); + } + + static Future cacheRefreshToken(String refreshToken){ + return SharedPreferenceHelper.set(SpConstant.token_refresh_key, refreshToken); + } + + static Future clearRefreshToken(){ + return SharedPreferenceHelper.set(SpConstant.token_refresh_key, ''); + } + +} \ No newline at end of file diff --git a/study-app-tutorials/lib/component/cache/user_caches.dart b/study-app-tutorials/lib/component/cache/user_caches.dart new file mode 100644 index 000000000..36bf77501 --- /dev/null +++ b/study-app-tutorials/lib/component/cache/user_caches.dart @@ -0,0 +1,25 @@ + +import 'package:tutorials/request/model/user.dart'; + +class UserCaches{ + + static int _userId = 0; + static User _user = User(); + + static int getUserId(){ + return _userId; + } + + static void cacheUserId(int userId){ + _userId = userId; + } + + static void cacheUser(User user){ + _user = user??_user; + _user.id = user.id??_userId; + } + + static User getUser(){ + return _user??User(); + } +} \ No newline at end of file diff --git a/study-app-tutorials/lib/component/dialog/dialogs.dart b/study-app-tutorials/lib/component/dialog/dialogs.dart new file mode 100644 index 000000000..6e77ad3a1 --- /dev/null +++ b/study-app-tutorials/lib/component/dialog/dialogs.dart @@ -0,0 +1,220 @@ +import 'package:tutorials/locale/translations.dart'; +import 'package:flutter/material.dart'; + +class Dialogs { + static Future showProgress( + BuildContext context, String title, double? value, onWillPop) { + return showDialog( + context: context, + barrierDismissible: false, //点击遮罩不关闭对话框 + builder: (context) { + return new WillPopScope( + onWillPop: onWillPop, //重点此举 + child: AlertDialog( + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + CircularProgressIndicator(value: value,), + Padding( + padding: const EdgeInsets.only(top: 26.0), + child: Text(title), + ) + ], + ), + )); + }); + } + + static Future dismiss(BuildContext context) async { + Navigator.of(context).pop(); + } + + static Future showInfoDialog( + BuildContext context, String? title, String content) { + TextEditingController _controller = TextEditingController(); + return showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: Text( + title ?? Translations.textOf(context, "all.dialog.info.title")), + content: Text(content), + actions: [ + FlatButton( + child: Text("确定"), + onPressed: () { + Navigator.of(context).pop(_controller.text.toString()); + }, + ), + ], + ); + }, + ); + } + + static Future showConfirmDialog( + BuildContext context, String title, String? content) { + List actions = [ + FlatButton( + child: Text("取消"), + onPressed: () => Navigator.of(context).pop(), // 关闭对话框 + ), + FlatButton( + child: Text("确定"), + onPressed: () { + Navigator.of(context).pop("true"); + }, + ), + ]; + if (content == null) { + return showDialog( + context: context, + builder: (context) { + return AlertDialog(title: Text(title), actions: actions); + }, + ); + } + return showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: Text(title), content: Text(content), actions: actions); + }, + ); + } + + // ref: https://stackoverflow.com/questions/53311553/how-to-set-showmodalbottomsheet-to-full-height + static Future showButtonSelectDialog( + BuildContext context, List contents, List? icons) { + return showModalBottomSheet( + isScrollControlled: true, + context: context, + isDismissible: true, + builder: (context) => Wrap(children: [ + ListView.builder( + itemCount: contents.length, + shrinkWrap: true, + itemBuilder: (BuildContext context, int index) { + return icons == null + ? ListTile( + title: Text(contents[index]), + onTap: () => Navigator.of(context).pop(index), + ) + : ListTile( + title: Text(contents[index]), + trailing: icons[index], + onTap: () => Navigator.of(context).pop(index), + ); + }) + ]), + ); + } + + static Future showInputDialog( + BuildContext context, String title, String defaultValue) { + TextEditingController _controller = TextEditingController.fromValue( + TextEditingValue( + text: defaultValue, + selection: TextSelection.fromPosition(TextPosition( + affinity: TextAffinity.downstream, + offset: defaultValue.length)))); + return showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: Text(title), + content: TextField( + controller: _controller, + decoration: InputDecoration( + contentPadding: EdgeInsets.all(10.0), //内容内边距 + ), + autofocus: true, //自动获取焦点,设置false + ), + actions: [ + FlatButton( + child: Text("取消"), + onPressed: () => Navigator.of(context).pop(), // 关闭对话框 + ), + FlatButton( + child: Text("确定"), + onPressed: () { + Navigator.of(context).pop(_controller.text.toString()); + }, + ), + ], + ); + }, + ); + } + + static Future showListBottomSheet( + BuildContext context, List list) { + return showModalBottomSheet( + context: context, + builder: (BuildContext context) { + return ListView.builder( + itemCount: list.length, + itemBuilder: (BuildContext context, int index) { + return ListTile( + title: Text(list[index]), + onTap: () => Navigator.of(context).pop(index), + ); + }, + ); + }, + ); + } + + static Future showInputBottomSheet( + BuildContext context, String helperText, String defaultValue) { + TextEditingController _controller = TextEditingController.fromValue( + TextEditingValue( + text: defaultValue, + selection: TextSelection.fromPosition(TextPosition( + affinity: TextAffinity.downstream, + offset: defaultValue.length)))); + return showModalBottomSheet( + context: context, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.horizontal( + left: Radius.circular(8), + right: Radius.circular(8), + )), + builder: (BuildContext context) { + return Container( + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TextField( + controller: _controller, + decoration: InputDecoration( + contentPadding: EdgeInsets.all(10.0), + helperText: helperText, + ), + autofocus: true, //自动获取焦点,设置false + ), + ], + )), + Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + IconButton( + icon: Icon( + Icons.send, + color: Colors.blue, + size: 20.0, + ), + onPressed: () { + Navigator.of(context) + .pop(_controller.text.toString()); + }), + ]) + ], + ), + ); + }); + } +} diff --git a/study-app-tutorials/lib/component/dialog/loading.dart b/study-app-tutorials/lib/component/dialog/loading.dart new file mode 100644 index 000000000..6ef6a109b --- /dev/null +++ b/study-app-tutorials/lib/component/dialog/loading.dart @@ -0,0 +1,18 @@ +import 'package:flutter_easyloading/flutter_easyloading.dart'; + +/// EasyLoading.show(status: 'loading...'); +// EasyLoading.showProgress(0.3, status: 'downloading...'); +// EasyLoading.showSuccess('Great Success!'); +// EasyLoading.showError('Failed with Error'); +// EasyLoading.showInfo('Useful Information.'); +// EasyLoading.showToast('Toast'); +// EasyLoading.dismiss(); +class Loading { + static void showLoading(String title, double value) { + EasyLoading.showProgress(value, status: title); + } + + static void dismiss() { + EasyLoading.dismiss(); + } +} diff --git a/study-app-tutorials/lib/component/dialog/progress_dialogs.dart b/study-app-tutorials/lib/component/dialog/progress_dialogs.dart new file mode 100644 index 000000000..9c2e45834 --- /dev/null +++ b/study-app-tutorials/lib/component/dialog/progress_dialogs.dart @@ -0,0 +1,25 @@ +import 'package:flutter/material.dart'; +import 'package:sn_progress_dialog/sn_progress_dialog.dart' as sn_progress_dialog; + +class ProgressDialog { + + late sn_progress_dialog.ProgressDialog _pd; + + ProgressDialog(BuildContext context){ + _pd = sn_progress_dialog.ProgressDialog(context: context); + } + + + void showProgress(String title, int max) { + _pd.show(max: max, msg: title); + } + + void updateProgress(int value) { + _pd.update(value: value); + } + + void dismiss() { + _pd.close(); + } + +} diff --git a/study-app-tutorials/lib/component/downloader/file_downloader.dart b/study-app-tutorials/lib/component/downloader/file_downloader.dart new file mode 100644 index 000000000..f519affe6 --- /dev/null +++ b/study-app-tutorials/lib/component/downloader/file_downloader.dart @@ -0,0 +1,8 @@ +import 'package:flutter_cache_manager/flutter_cache_manager.dart'; + +class FileDownloader { + + static Stream download(String url) { + return DefaultCacheManager().getFileStream(url, withProgress: true); + } +} diff --git a/study-app-tutorials/lib/component/event/base_event.dart b/study-app-tutorials/lib/component/event/base_event.dart new file mode 100644 index 000000000..4e53e9c0b --- /dev/null +++ b/study-app-tutorials/lib/component/event/base_event.dart @@ -0,0 +1,3 @@ + + +class BaseEvent {} diff --git a/study-app-tutorials/lib/component/event/event_bus.dart b/study-app-tutorials/lib/component/event/event_bus.dart new file mode 100644 index 000000000..4db7757de --- /dev/null +++ b/study-app-tutorials/lib/component/event/event_bus.dart @@ -0,0 +1,18 @@ +import 'package:event_bus/event_bus.dart' as marcojakob; + +typedef EventCallback = void Function(dynamic event); + +class EventBus { + static marcojakob.EventBus _eventBus = marcojakob.EventBus(); + + static void publish(event) { + _eventBus.fire(event); + } + + static void consume(EventCallback callback) { + _eventBus.on().listen((event) { + // All events are of type UserLoggedInEvent (or subtypes of it). + callback(event); + }); + } +} diff --git a/study-app-tutorials/lib/component/http/cancel_requests.dart b/study-app-tutorials/lib/component/http/cancel_requests.dart new file mode 100644 index 000000000..0c72d19c9 --- /dev/null +++ b/study-app-tutorials/lib/component/http/cancel_requests.dart @@ -0,0 +1,11 @@ +import 'package:dio/dio.dart'; + +class CancelRequests { + + CancelToken token = CancelToken(); + + void cancel([dynamic reason]) { + token.cancel(reason); + } + +} diff --git a/study-app-tutorials/lib/component/http/http_byte_result.dart b/study-app-tutorials/lib/component/http/http_byte_result.dart new file mode 100644 index 000000000..a4f435ab1 --- /dev/null +++ b/study-app-tutorials/lib/component/http/http_byte_result.dart @@ -0,0 +1,7 @@ +import 'dart:collection'; + +class HttpByteResult { + int statusCode = 0; + var responseBytes; + Map> headers = HashMap(); +} diff --git a/study-app-tutorials/lib/component/http/http_origin_result.dart b/study-app-tutorials/lib/component/http/http_origin_result.dart new file mode 100644 index 000000000..dcc82e1d3 --- /dev/null +++ b/study-app-tutorials/lib/component/http/http_origin_result.dart @@ -0,0 +1,49 @@ +class HttpOriginResult { + int? code; + int? originCode; + String? msg; + Null? originMsg; + Data? data; + + HttpOriginResult( + {this.code, this.originCode, this.msg, this.originMsg, this.data}); + + HttpOriginResult.fromJson(Map json) { + code = json['code']; + originCode = json['originCode']; + msg = json['msg']; + originMsg = json['originMsg']; + data = json['data'] != null ? new Data.fromJson(json['data']) : null; + } + + Map toJson() { + final Map data = new Map(); + data['code'] = this.code; + data['originCode'] = this.originCode; + data['msg'] = this.msg; + data['originMsg'] = this.originMsg; + if (this.data != null) { + data['data'] = this.data!.toJson(); + } + return data; + } +} + +class Data { + String? error; + String? errorDescription; + + Data({this.error, this.errorDescription}); + + Data.fromJson(Map json) { + error = json['error']; + errorDescription = json['error_description']; + } + + Map toJson() { + final Map data = new Map(); + data['error'] = this.error; + data['error_description'] = this.errorDescription; + return data; + } +} diff --git a/study-app-tutorials/lib/component/http/http_progress_callback.dart b/study-app-tutorials/lib/component/http/http_progress_callback.dart new file mode 100644 index 000000000..9485d20eb --- /dev/null +++ b/study-app-tutorials/lib/component/http/http_progress_callback.dart @@ -0,0 +1,2 @@ + +typedef HttpProgressCallback = void Function(int count, int total); diff --git a/study-app-tutorials/lib/component/http/http_requests.dart b/study-app-tutorials/lib/component/http/http_requests.dart new file mode 100644 index 000000000..babe87cf2 --- /dev/null +++ b/study-app-tutorials/lib/component/http/http_requests.dart @@ -0,0 +1,265 @@ +import 'dart:convert'; + +import 'package:tutorials/component/http/cancel_requests.dart'; +import 'package:tutorials/component/http/http_byte_result.dart'; +import 'package:tutorials/component/http/http_origin_result.dart'; +import 'package:tutorials/component/http/http_progress_callback.dart'; +import 'package:tutorials/component/http/http_result.dart'; +import 'package:tutorials/component/http/network_check_interceptors.dart'; +import 'package:tutorials/component/log/logs.dart'; +import 'package:tutorials/constant/http_constant.dart'; +import 'package:tutorials/prop/http_prop.dart'; +import 'package:dio/dio.dart'; + +class HttpRequests { + static final Dio _dio = Dio(); + + static void init() { + _dio.interceptors.add(NetworkCheckInterceptors()); + } + + static Future upload( + String path, + Map? parameters, + List files, + Map? headers, + HttpProgressCallback onProgress, + CancelRequests cancelRequests) async { + return _doUploadRequest( + path, parameters, files, headers, onProgress, cancelRequests); + } + + static Future getBytes( + String path, + Map? parameters, + Map? headers, + HttpProgressCallback onProgress, + CancelRequests cancelRequests) async { + return _doDownloadRequest( + path, parameters, null, headers, "get", onProgress, cancelRequests); + } + + static Future postBytes( + String path, + Map parameters, + Map headers, + HttpProgressCallback onProgress, + CancelRequests cancelRequests) async { + return _doDownloadRequest( + path, parameters, null, headers, "post", onProgress, cancelRequests); + } + + static Future get(String path, Map? parameters, + Map? headers) async { + return _doBaseRequest(path, parameters, null, headers, "get", null); + } + + static Future post(String path, Map? parameters, + Map? headers) async { + return _doBaseRequest(path, parameters, null, headers, "post", null); + } + + static Future postRaw( + String path, dynamic data, Map headers) async { + return _doBaseRequest(path, null, data, headers, "post", null); + } + + static Future getWith( + String path, + Map parameters, + Map headers, + int timeoutMilliseconds) async { + return _doBaseRequest( + path, parameters, null, headers, "get", timeoutMilliseconds); + } + + static Future postWith( + String path, + Map parameters, + Map headers, + int timeoutMilliseconds) async { + return _doBaseRequest( + path, parameters, null, headers, "post", timeoutMilliseconds); + } + + static Future postRawWith(String path, dynamic data, + Map headers, int timeoutMilliseconds) async { + return _doBaseRequest( + path, null, data, headers, "post", timeoutMilliseconds); + } + + static Future _doDownloadRequest( + String path, + Map? parameters, + dynamic data, + Map? headers, + String method, + HttpProgressCallback onProgress, + CancelRequests cancelRequests) async { + HttpByteResult result = HttpByteResult(); + try { + path = rebuildUrl(path); + Logs.info('_doDownloadRequest path = ${path}'); + Logs.info('_doDownloadRequest parameters = ${parameters}'); + Logs.info('_doDownloadRequest data = ${data}'); + _dio.options = _buildDownloadOption(parameters, data, headers); + Response response; + if ("get".compareTo(method) == 0) { + response = await _dio.get(path, + queryParameters: parameters, cancelToken: cancelRequests.token, + onReceiveProgress: (int sent, int total) { + Logs.info("_doDownloadRequest onReceiveProgress ${sent} / ${total}"); + onProgress?.call(sent, total); + }); + } else { + response = await _dio.post(path, + queryParameters: parameters, + cancelToken: cancelRequests.token, + data: data, onReceiveProgress: (int sent, int total) { + Logs.info("_doDownloadRequest onReceiveProgress ${sent} / ${total}"); + onProgress?.call(sent, total); + }); + } + Logs.info('_doDownloadRequest statusCode = ${response.statusCode}'); + result.responseBytes = response.data; + result.statusCode = response.statusCode ?? 500; + result.headers = response.headers.map; + return result; + } catch (e) { + Logs.info('_doDownloadRequest error' + e.toString()); + result.statusCode = HttpConstant.server_not_response; + } + return result; + } + + static Future _doUploadRequest( + String path, + Map? parameters, + List files, + Map? headers, + HttpProgressCallback onProgress, + CancelRequests cancelRequests) async { + HttpByteResult result = HttpByteResult(); + try { + path = rebuildUrl(path); + Logs.info('_doUploadRequest path = ${path}'); + Logs.info('_doUploadRequest parameters = ${parameters}'); + Logs.info('_doUploadRequest files.length = ${files?.length}'); + + Map paramMap = parameters ?? {}; + paramMap['files'] = files; + FormData formData = FormData.fromMap(paramMap); + + _dio.options = BaseOptions( + baseUrl: HttpProp.getBaseUrl(), + headers: headers, + contentType: HttpProp.getContentType(), + followRedirects: false, + validateStatus: (status) { + return (status ?? 500) < 500; + }); + Response response = await _dio.post(path, + queryParameters: parameters, + cancelToken: cancelRequests.token, + data: formData, onReceiveProgress: (int sent, int total) { + Logs.info("_doUploadRequest onReceiveProgress ${sent} / ${total}"); + onProgress?.call(sent, total); + }); + Logs.info('_doUploadRequest statusCode = ${response.statusCode}'); + result.responseBytes = response.data; + result.statusCode = response.statusCode ?? 500; + result.headers = response.headers.map; + return result; + } catch (e) { + Logs.info('_doUploadRequest error' + e.toString()); + result.statusCode = HttpConstant.server_not_response; + } + return result; + } + + static Future _doBaseRequest( + String path, + Map? parameters, + dynamic? data, + Map? headers, + String method, + int? timeoutMilliseconds) async { + HttpResult result = HttpResult(); + try { + path = rebuildUrl(path); + Logs.info('_doBaseRequest path = ${path}'); + Logs.info('_doBaseRequest parameters = ${parameters}'); + Logs.info('_doBaseRequest data = ${data}'); + _dio.options = _buildOption( + parameters ?? {}, data, headers ?? {}, timeoutMilliseconds ?? 6000); + Response response; + if ("get".compareTo(method) == 0) { + response = await _dio.get(path, queryParameters: parameters); + } else { + response = + await _dio.post(path, queryParameters: parameters, data: data); + } + Logs.info('_doBaseRequest statusCode = ${response.statusCode}'); + result.responseBody = + response.data == null || "".compareTo(response.data) == 0 + ? "{}" + : response.data; + result.statusCode = response.statusCode ?? 500; + result.headers = response.headers.map; + return result; + } on DioError catch (e) { + Logs.info('_doBaseRequest DioError ${e.toString()}' ); + Logs.info('_doBaseRequest DioError data ${e.response?.data}'); + result.statusCode = e.response?.statusCode??HttpConstant.server_not_response; + result.responseBody = '{ "msg":"${e.response?.statusMessage??HttpConstant.server_not_response_msg}" }'; + + try{ + if(e.response?.statusCode == 400 && (e?.response?.statusMessage?.isEmpty??true)){ + HttpOriginResult originResult = HttpOriginResult.fromJson(jsonDecode(e.response?.data)); + result.responseBody = '{ "msg":"${originResult.data?.errorDescription??HttpConstant.server_not_response_msg}" }'; + } + }catch(e){ + Logs.info(e.toString()); + } + + } catch (e) { + Logs.info('_doBaseRequest error ${e.toString()}' ); + result.statusCode = HttpConstant.server_not_response; + result.responseBody = '{ "msg":"${HttpConstant.server_not_response_msg}" }'; + } + return result; + } + + static BaseOptions _buildDownloadOption(Map? parameters, + dynamic data, Map? headers) { + return BaseOptions( + baseUrl: HttpProp.getBaseUrl(), +// receiveTimeout: HttpProp.timeout, // 不需要指定超时时间 + headers: headers, + contentType: HttpProp.getContentType(), + responseType: ResponseType.bytes, + followRedirects: false, + validateStatus: (status) { + return (status ?? 500) < 500; + }); + } + + static BaseOptions _buildOption(Map parameters, dynamic data, + Map headers, int timeoutMilliseconds) { + return BaseOptions( + baseUrl: HttpProp.getBaseUrl(), + connectTimeout: HttpProp.getTimeout(), + receiveTimeout: HttpProp.getTimeout(), + headers: headers, + contentType: HttpProp.getContentType(), + responseType: ResponseType.plain, + ); + } + + static String rebuildUrl(String url) { + if (url.startsWith("/") && HttpProp.getBaseUrl().endsWith("/")) { + url = url.substring(1); + } + return url.startsWith("http") ? url : HttpProp.getBaseUrl() + url; + } +} diff --git a/study-app-tutorials/lib/component/http/http_result.dart b/study-app-tutorials/lib/component/http/http_result.dart new file mode 100644 index 000000000..970d3bd51 --- /dev/null +++ b/study-app-tutorials/lib/component/http/http_result.dart @@ -0,0 +1,7 @@ +import 'dart:collection'; + +class HttpResult { + int statusCode = 0; + String responseBody = ""; + Map> headers = HashMap(); +} diff --git a/study-app-tutorials/lib/component/http/network_check_interceptors.dart b/study-app-tutorials/lib/component/http/network_check_interceptors.dart new file mode 100644 index 000000000..e70c66f20 --- /dev/null +++ b/study-app-tutorials/lib/component/http/network_check_interceptors.dart @@ -0,0 +1,31 @@ +import 'package:tutorials/component/toast/Toasts.dart'; +import 'package:tutorials/utils/app_utils.dart'; +import 'package:dio/dio.dart'; + +class NetworkCheckInterceptors implements InterceptorsWrapper { + + NetworkCheckInterceptors(){ + } + + + @override + void onError(DioError err, ErrorInterceptorHandler handler) { + AppUtils.isConnected().then((value) => { + if(!value){ + Toasts.show('请先连接网络') + } + }); + handler.next(err); //continue + } + + @override + void onRequest(RequestOptions options, RequestInterceptorHandler handler) { + handler.next(options); //continue + } + + @override + void onResponse( + Response response, ResponseInterceptorHandler handler) { + handler.next(response); //continue + } +} diff --git a/study-app-tutorials/lib/component/image.crop/image_cropper.dart b/study-app-tutorials/lib/component/image.crop/image_cropper.dart new file mode 100644 index 000000000..bf17f1b08 --- /dev/null +++ b/study-app-tutorials/lib/component/image.crop/image_cropper.dart @@ -0,0 +1,30 @@ +import 'package:flutter/material.dart'; +import 'package:image_cropper/image_cropper.dart' as origin_image_cropper; +import 'package:tutorials/locale/translations.dart'; + +class ImageCropper { + static Future cropImage( + BuildContext context, String valueCrop) async { + String _title = Translations.textOf(context, "component.crop.title"); + + final croppedFile = await origin_image_cropper.ImageCropper().cropImage( + sourcePath: valueCrop, + compressFormat: origin_image_cropper.ImageCompressFormat.jpg, + compressQuality: 100, + androidUiSettings: origin_image_cropper.AndroidUiSettings( + toolbarTitle: _title, + toolbarColor: Colors.blue, + toolbarWidgetColor: Colors.white, + initAspectRatio: origin_image_cropper.CropAspectRatioPreset.original, + hideBottomControls: true, + lockAspectRatio: false), + iosUiSettings: origin_image_cropper.IOSUiSettings( + title: _title, + ), + ); + if (croppedFile != null) { + return croppedFile.path; + } + return valueCrop; + } +} diff --git a/study-app-tutorials/lib/component/log/logs.dart b/study-app-tutorials/lib/component/log/logs.dart new file mode 100644 index 000000000..a9cbab6a1 --- /dev/null +++ b/study-app-tutorials/lib/component/log/logs.dart @@ -0,0 +1,9 @@ +class Logs { + + /** + * 打印日志 + */ + static void info(String msg) { + print(msg); + } +} diff --git a/study-app-tutorials/lib/component/permission/permission_helper.dart b/study-app-tutorials/lib/component/permission/permission_helper.dart new file mode 100644 index 000000000..710589929 --- /dev/null +++ b/study-app-tutorials/lib/component/permission/permission_helper.dart @@ -0,0 +1,21 @@ +import 'package:tutorials/component/log/logs.dart'; +import 'package:permission_handler/permission_handler.dart'; + +class PermissionHelper { + static Future requestPermissions() async { + Map statuses = + await [Permission.storage,Permission.activityRecognition].request(); + bool result = true; + statuses.forEach((key, value) { + if (!value.isGranted) { + Logs.info('${key} is not Granted!!'); + result = false; + } + }); + return result; + } + + static Future _requestPermission(Permission permission) async { + return permission.request().isGranted; + } +} diff --git a/study-app-tutorials/lib/component/picker/file_picker.dart b/study-app-tutorials/lib/component/picker/file_picker.dart new file mode 100644 index 000000000..074d00df6 --- /dev/null +++ b/study-app-tutorials/lib/component/picker/file_picker.dart @@ -0,0 +1,10 @@ +import 'dart:io'; + +import 'package:file_picker/file_picker.dart' as origin_file_picker; + +/// https://pub.flutter-io.cn/packages/file_picker +class FilePicker { + static Future pickDirectory() async { + return await origin_file_picker.FilePicker.platform.getDirectoryPath(); + } +} diff --git a/study-app-tutorials/lib/component/picker/image_picker.dart b/study-app-tutorials/lib/component/picker/image_picker.dart new file mode 100644 index 000000000..b91fbd11d --- /dev/null +++ b/study-app-tutorials/lib/component/picker/image_picker.dart @@ -0,0 +1,23 @@ +import 'package:image_picker/image_picker.dart' as origin_image_picker; +import 'package:image_picker/image_picker.dart'; + +// final ImagePicker _picker = ImagePicker(); +// // Pick an image +// final XFile? image = await _picker.pickImage(source: ImageSource.gallery); +// // Capture a photo +// final XFile? photo = await _picker.pickImage(source: ImageSource.camera); +// // Pick a video +// final XFile? image = await _picker.pickVideo(source: ImageSource.gallery); +// // Capture a video +// final XFile? video = await _picker.pickVideo(source: ImageSource.camera); +// // Pick multiple images +// final List? images = await _picker.pickMultiImage(); +class ImagePicker { + static Future pickImage() async { + final origin_image_picker.ImagePicker _picker = + origin_image_picker.ImagePicker(); +// Pick an image + final XFile? image = await _picker.pickImage(source: ImageSource.gallery); + return image?.path; + } +} diff --git a/study-app-tutorials/lib/component/sp/shared_preference_helper.dart b/study-app-tutorials/lib/component/sp/shared_preference_helper.dart new file mode 100644 index 000000000..a793c9cee --- /dev/null +++ b/study-app-tutorials/lib/component/sp/shared_preference_helper.dart @@ -0,0 +1,17 @@ +import 'package:shared_preferences/shared_preferences.dart'; + +class SharedPreferenceHelper { + static Future _getInstance() { + return SharedPreferences.getInstance(); + } + + static Future set(String key, String value) async { + SharedPreferences _prefs = await _getInstance(); + return _prefs.setString(key, value); + } + + static Future get(String key, String defaultValue) async { + SharedPreferences _prefs = await _getInstance(); + return _prefs.getString(key)??defaultValue; + } +} diff --git a/study-app-tutorials/lib/component/toast/Toasts.dart b/study-app-tutorials/lib/component/toast/Toasts.dart new file mode 100644 index 000000000..c2aba250a --- /dev/null +++ b/study-app-tutorials/lib/component/toast/Toasts.dart @@ -0,0 +1,15 @@ +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; + +class Toasts { + static void show(String msg) { + Fluttertoast.showToast( + msg: msg, + toastLength: Toast.LENGTH_SHORT, + gravity: ToastGravity.CENTER, + timeInSecForIosWeb: 1, + backgroundColor: Colors.black, + textColor: Colors.white, + fontSize: 16.0); + } +} diff --git a/study-app-tutorials/lib/constant/app_constant.dart b/study-app-tutorials/lib/constant/app_constant.dart new file mode 100644 index 000000000..a868bc754 --- /dev/null +++ b/study-app-tutorials/lib/constant/app_constant.dart @@ -0,0 +1,10 @@ + + +const double galleryHeaderHeight = 64; + +const int appVersion = 1; + + + + + diff --git a/study-app-tutorials/lib/constant/color_constant.dart b/study-app-tutorials/lib/constant/color_constant.dart new file mode 100644 index 000000000..2a283b41c --- /dev/null +++ b/study-app-tutorials/lib/constant/color_constant.dart @@ -0,0 +1,12 @@ + +import 'package:flutter/material.dart'; + +class ColorConstant { + + static Color app_bar_only_back_color = Colors.black; + +} + + + + diff --git a/study-app-tutorials/lib/constant/error_code_constant.dart b/study-app-tutorials/lib/constant/error_code_constant.dart new file mode 100644 index 000000000..e010538d5 --- /dev/null +++ b/study-app-tutorials/lib/constant/error_code_constant.dart @@ -0,0 +1,12 @@ + + +class ErrorCodeConstant{ + + static int success = 200; + static int unknown = 999; + + static int loginSecurityCodeRequired = 1001; + +} + + diff --git a/study-app-tutorials/lib/constant/http_constant.dart b/study-app-tutorials/lib/constant/http_constant.dart new file mode 100644 index 000000000..4f00bdee2 --- /dev/null +++ b/study-app-tutorials/lib/constant/http_constant.dart @@ -0,0 +1,25 @@ + + +class HttpConstant{ + + static int server_not_response = 205; + static String server_not_response_msg = 'server not response'; + + static String url_login = "/oauth/token"; + static String url_register = "/oauth/token"; + static String url_user_profile = "/api/authUser/profile/detail"; + static String url_validate_code = "/validate/code/generate"; + + static String url_tv_query_page = "/ignore/api/tv/query/page"; + static String url_tv_set_favorite = "/ignore/android/tv/favorite/set"; + static String url_settings_version_check = "/ignore/android/app/version/check/{version}"; + static String url_settings_app_download = "/ignore/android/app/version/check/{version}"; + static String url_settings_app_latest = "/ignore/android/app/version/latest"; + static String url_settings_app_versions = "/ignore/android/app/version/query"; + static String url_settings_app_feedback_query = "/ignore/feedbackMessage/query/page"; + static String url_settings_app_feedback_insert = "/ignore/feedbackMessage/insert"; + static String url_settings_app_feedback_delete = "/ignore/feedbackMessage/delete"; + +} + + diff --git a/study-app-tutorials/lib/constant/locale_constant.dart b/study-app-tutorials/lib/constant/locale_constant.dart new file mode 100644 index 000000000..b2c0b7648 --- /dev/null +++ b/study-app-tutorials/lib/constant/locale_constant.dart @@ -0,0 +1,13 @@ + +class LocaleConstant{ + + static String settings_upgrade_dialog = "settings.upgrade.dialog.title"; + static String settings_upgrade_cancel = "settings.upgrade.cancel"; + + static String all_delete_confirm = "all.delete.confirm"; + + static String home_back_confirm = "home.back.confirm"; + +} + + diff --git a/study-app-tutorials/lib/constant/route_constant.dart b/study-app-tutorials/lib/constant/route_constant.dart new file mode 100644 index 000000000..429c892f4 --- /dev/null +++ b/study-app-tutorials/lib/constant/route_constant.dart @@ -0,0 +1,38 @@ + + +class RouteNameConstant{ + + static String route_name_home = "home"; + static String route_name_home_statistics = "home/statistics"; + static String route_name_settings = "settings"; + static String route_name_setting_dev_tool = "settings/develop"; + static String route_name_setting_feedbacks = "settings/feedbacks"; + static String route_name_about = "about"; + static String route_name_about_versions = "about/versions"; + static String route_name_about_author = "about/author"; + static String route_name_apks = "apks"; + + static String route_name_family_events = "family/events"; + + static String route_name_login = "login"; + static String route_name_register = "register"; + static String route_name_register_verify = "register/verify"; + static String route_name_register_finish = "register/finish"; + + static String route_name_profile = "profile"; + static String route_name_forget_password = "forget/password"; + static String route_name_forget_password_verify = "forget/password/verify"; + static String route_name_forget_password_finish = "forget/password/finish"; + + + static String route_name_name_card = "name/card"; + static String route_name_name_card_edit = "name/card/edit"; + + static String route_name_env = "env/setting"; + static String route_name_env_edit = "env/edit"; + + + +} + + diff --git a/study-app-tutorials/lib/constant/sp_constant.dart b/study-app-tutorials/lib/constant/sp_constant.dart new file mode 100644 index 000000000..33c4884a7 --- /dev/null +++ b/study-app-tutorials/lib/constant/sp_constant.dart @@ -0,0 +1,22 @@ +class SpConstant { + + static String protocol_key = "_protocol_key"; + static String protocol_value_default = "http"; + static String host_key = '_host_key'; + static String host_value_default = '101.132.119.250'; + static String port_key = '_port_key'; + static String port_value_default = '8088'; + + static String environment_index = '_environment_index'; + + static String download_dir_cache_key = 'download_dir_cache_key'; + static String download_dir_value_default = '/storage/emulated/0/Download'; + + static String token_access_key = '/token/access/key'; + static String token_refresh_key = '/token/refresh/key'; + + + static String mock_switch_cache_key = 'mock_switch_cache_key'; + static String mock_switch_value_default = 'false'; + +} diff --git a/study-app-tutorials/lib/locale/supported_locales.dart b/study-app-tutorials/lib/locale/supported_locales.dart new file mode 100644 index 000000000..8b89dd1d9 --- /dev/null +++ b/study-app-tutorials/lib/locale/supported_locales.dart @@ -0,0 +1,13 @@ +import 'package:flutter/material.dart'; + +class SupportedLocales { + static Locale zhLocale = const Locale('zh', 'CN'); // 中文简体 + static Locale enLocale = const Locale('en', 'US'); // 美国英语 + + static Locale defaultLocale = zhLocale; // 中文简体 + + static List locales = [ + zhLocale, + enLocale, + ]; +} diff --git a/study-app-tutorials/lib/locale/translations.dart b/study-app-tutorials/lib/locale/translations.dart new file mode 100644 index 000000000..20c31cea3 --- /dev/null +++ b/study-app-tutorials/lib/locale/translations.dart @@ -0,0 +1,66 @@ +import 'dart:async'; +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart' show rootBundle; +import 'package:tutorials/component/log/logs.dart'; +import 'package:tutorials/locale/supported_locales.dart'; + +class Translations { + Translations(Locale locale) { + this.locale = locale; + } + + Locale? locale; + static Map _localizedValues = {}; + + static String textOf(BuildContext context, String key) { + var text = _of(context)?.text(key); + return text ?? ""; + } + + static Translations? _of(BuildContext context) { + return Localizations.of(context, Translations); + } + + String text(String key) { + if(_localizedValues[key] == null){ + Logs.info('** $key not found'); + } + return _localizedValues[key] ?? 'Unknown'; + } + + static Future load(Locale locale) async { + Logs.info("load is called..."); + Translations translations = Translations(locale); + String jsonContent = await rootBundle + .loadString("assets/locale/i18n_${locale.languageCode}.json"); + _localizedValues = json.decode(jsonContent); + return translations; + } + + get currentLanguage => locale?.languageCode; +} + +class TranslationsDelegate extends LocalizationsDelegate { + const TranslationsDelegate(); + + @override + bool isSupported(Locale locale) { + List locales = SupportedLocales.locales; + for (Locale locale in locales) { + if (locale.languageCode == locale.languageCode) { + return true; + } + } + return false; + } + + @override + Future load(Locale locale) => Translations.load(locale); + + @override + bool shouldReload(TranslationsDelegate old) => false; + + static TranslationsDelegate delegate = const TranslationsDelegate(); +} diff --git a/study-app-tutorials/lib/main.dart b/study-app-tutorials/lib/main.dart new file mode 100644 index 000000000..0d432c6e6 --- /dev/null +++ b/study-app-tutorials/lib/main.dart @@ -0,0 +1,150 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_easyloading/flutter_easyloading.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:tutorials/component/permission/permission_helper.dart'; +import 'package:tutorials/constant/route_constant.dart'; +import 'package:tutorials/locale/supported_locales.dart'; +import 'package:tutorials/locale/translations.dart'; +import 'package:tutorials/route/route.dart'; +import 'package:tutorials/startup/application.dart'; +import 'package:tutorials/component/log/logs.dart'; + +void main() { + runApp(MyApp()); +} + +class MyApp extends StatelessWidget { + // This widget is the root of your application. + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'hello', + theme: ThemeData( + // This is the theme of your application. + // + // Try running your application with "flutter run". You'll see the + // application has a blue toolbar. Then, without quitting the app, try + // changing the primarySwatch below to Colors.green and then invoke + // "hot reload" (press "r" in the console where you ran "flutter run", + // or simply save your changes to "hot reload" in a Flutter IDE). + // Notice that the counter didn't reset back to zero; the application + // is not restarted. + primarySwatch: Colors.blue, + // This makes the visual density adapt to the platform that you run + // the app on. For desktop platforms, the controls will be smaller and + // closer together (more dense) than on mobile platforms. + visualDensity: VisualDensity.adaptivePlatformDensity, + ), + home: MyHomePage(title: '小陆'), + // routes: RouteConfiguration.routes, + + // 路由 + onGenerateRoute: RouteConfiguration.onGenerateRoute, + + locale: SupportedLocales.defaultLocale, //手动指定locale + + localizationsDelegates: [ + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + TranslationsDelegate.delegate, + ], + supportedLocales: SupportedLocales.locales, + localeListResolutionCallback: (locale, supportedLocales) { + return null; // 这里必须返回null,CIA + }, + builder: EasyLoading.init(), + ); + + } +} + +class MyHomePage extends StatefulWidget { + MyHomePage({Key? key,required this.title}) : super(key: key); + + // This widget is the home page of your application. It is stateful, meaning + // that it has a State object (defined below) that contains fields that affect + // how it looks. + + // This class is the configuration for the state. It holds the values (in this + // case the title) provided by the parent (in this case the App widget) and + // used by the build method of the State. Fields in a Widget subclass are + // always marked "final". + + final String title; + + @override + _MyHomePageState createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + @override + void initState() { + // TODO: implement initState + super.initState(); + checkPermission(); + } + + void checkPermission() async { + bool isGranted = await PermissionHelper.requestPermissions()??false; + Logs.info('isGranted0 = ' + isGranted.toString()); + + if (isGranted) { + Application.init(context); + Navigator.of(context).pop(1); + toDefaultPage(); + } else { + Navigator.of(context).pop(1); + } + } + + void toDefaultPage() { + Navigator.of(context).pushNamed(RouteNameConstant.route_name_login, arguments: "hi"); + } + + @override + Widget build(BuildContext context) { + // This method is rerun every time setState is called, for instance as done + // by the toHome method above. + // + // The Flutter framework has been optimized to make rerunning build methods + // fast, so that you can just rebuild anything that needs updating rather + // than having to individually change instances of widgets. + return Scaffold( + appBar: AppBar( + // Here we take the value from the MyHomePage object that was created by + // the App.build method, and use it to set our appbar title. + title: Text(widget.title), + ), + body: Center( + // Center is a layout widget. It takes a single child and positions it + // in the middle of the parent. + child: Column( + // Column is also a layout widget. It takes a list of children and + // arranges them vertically. By default, it sizes itself to fit its + // children horizontally, and tries to be as tall as its parent. + // + // Invoke "debug painting" (press "p" in the console, choose the + // "Toggle Debug Paint" action from the Flutter Inspector in Android + // Studio, or the "Toggle Debug Paint" command in Visual Studio Code) + // to see the wireframe for each widget. + // + // Column has various properties to control how it sizes itself and + // how it positions its children. Here we use mainAxisAlignment to + // center the children vertically; the main axis here is the vertical + // axis because Columns are vertical (the cross axis would be + // horizontal). + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text(Translations.textOf(context,"main.title"), + ), + ], + ), + ), + floatingActionButton: FloatingActionButton( + onPressed: toDefaultPage, + tooltip: 'Main', + child: const Icon(Icons.arrow_forward_rounded), + ), // This trailing comma makes auto-formatting nicer for build methods. + ); + } +} diff --git a/study-app-tutorials/lib/model/about_author_tab.dart b/study-app-tutorials/lib/model/about_author_tab.dart new file mode 100644 index 000000000..75deba5ec --- /dev/null +++ b/study-app-tutorials/lib/model/about_author_tab.dart @@ -0,0 +1,8 @@ + + +class TabTitle { + String key = ''; + String text = ''; + + TabTitle(this.key, this.text); +} diff --git a/study-app-tutorials/lib/model/app_feedback.dart b/study-app-tutorials/lib/model/app_feedback.dart new file mode 100644 index 000000000..9a33bc0ef --- /dev/null +++ b/study-app-tutorials/lib/model/app_feedback.dart @@ -0,0 +1,72 @@ +class AppFeedback { + int? id; + int? fromUserId; + String? fromUserName; + int? toUserId; + String? toUserName; + String? msgContent; + String? readStatus; + String? sendTime; + String? readTime; + String? msgType; + String? createTime; + String? updateTime; + int? createStaffId; + int? updateStaffId; + String? deleteState; + + AppFeedback( + {this.id, + this.fromUserId, + this.fromUserName, + this.toUserId, + this.toUserName, + this.msgContent, + this.readStatus, + this.sendTime, + this.readTime, + this.msgType, + this.createTime, + this.updateTime, + this.createStaffId, + this.updateStaffId, + this.deleteState}); + + AppFeedback.fromJson(Map json) { + id = json['id']; + fromUserId = json['fromUserId']; + fromUserName = json['fromUserName']; + toUserId = json['toUserId']; + toUserName = json['toUserName']; + msgContent = json['msgContent']; + readStatus = json['readStatus']; + sendTime = json['sendTime']; + readTime = json['readTime']; + msgType = json['msgType']; + createTime = json['createTime']; + updateTime = json['updateTime']; + createStaffId = json['createStaffId']; + updateStaffId = json['updateStaffId']; + deleteState = json['deleteState']; + } + + Map toJson() { + final Map data = new Map(); + data['id'] = this.id; + data['fromUserId'] = this.fromUserId; + data['fromUserName'] = this.fromUserName; + data['toUserId'] = this.toUserId; + data['toUserName'] = this.toUserName; + data['msgContent'] = this.msgContent; + data['readStatus'] = this.readStatus; + data['sendTime'] = this.sendTime; + data['readTime'] = this.readTime; + data['msgType'] = this.msgType; + data['createTime'] = this.createTime; + data['updateTime'] = this.updateTime; + data['createStaffId'] = this.createStaffId; + data['updateStaffId'] = this.updateStaffId; + data['deleteState'] = this.deleteState; + return data; + } +} diff --git a/study-app-tutorials/lib/model/app_version.dart b/study-app-tutorials/lib/model/app_version.dart new file mode 100644 index 000000000..ef0aad0df --- /dev/null +++ b/study-app-tutorials/lib/model/app_version.dart @@ -0,0 +1,72 @@ + +class AppVersion { + int? id; + int? versionCode; + String? description; + int? fileId; + String? fileUrl; + String? versionName; + int? fileSize; + String? updateType; + DateTime? createTime; + Null? updateTime; + Null? createStaffId; + Null? updateStaffId; + Null? deleteState; + + AppVersion( + {this.id, + this.versionCode, + this.description, + this.fileId, + this.fileUrl, + this.versionName, + this.fileSize, + this.updateType, + this.createTime, + this.updateTime, + this.createStaffId, + this.updateStaffId, + this.deleteState}); + + static AppVersion getCurrentVersionInfo(){ + AppVersion appVersion = new AppVersion(); + appVersion.versionCode = 1; + appVersion.versionName = "v1.0.0"; + return appVersion; + } + + AppVersion.fromJson(Map json) { + id = json['id']; + versionCode = json['versionCode']; + description = json['description']; + fileId = json['fileId']; + fileUrl = json['fileUrl']; + versionName = json['versionName']; + fileSize = json['fileSize']; + updateType = json['updateType']; + createTime = json['createTime']; + updateTime = json['updateTime']; + createStaffId = json['createStaffId']; + updateStaffId = json['updateStaffId']; + deleteState = json['deleteState']; + } + + Map toJson() { + final Map data = new Map(); + data['id'] = this.id; + data['versionCode'] = this.versionCode; + data['description'] = this.description; + data['fileId'] = this.fileId; + data['fileUrl'] = this.fileUrl; + data['versionName'] = this.versionName; + data['fileSize'] = this.fileSize; + data['updateType'] = this.updateType; + data['createTime'] = this.createTime; + data['updateTime'] = this.updateTime; + data['createStaffId'] = this.createStaffId; + data['updateStaffId'] = this.updateStaffId; + data['deleteState'] = this.deleteState; + return data; + } +} diff --git a/study-app-tutorials/lib/model/pagination.dart b/study-app-tutorials/lib/model/pagination.dart new file mode 100644 index 000000000..1b75409f8 --- /dev/null +++ b/study-app-tutorials/lib/model/pagination.dart @@ -0,0 +1,22 @@ + +class Pagination { + int? targetPage; + int? pageSize; + int? total; + + Pagination({this.targetPage, this.pageSize, this.total}); + + Pagination.fromJson(Map json) { + targetPage = json['targetPage']; + pageSize = json['pageSize']; + total = json['total']; + } + + Map toJson() { + final Map data = new Map(); + data['targetPage'] = this.targetPage; + data['pageSize'] = this.pageSize; + data['total'] = this.total; + return data; + } +} \ No newline at end of file diff --git a/study-app-tutorials/lib/model/request_host.dart b/study-app-tutorials/lib/model/request_host.dart new file mode 100644 index 000000000..48cab4642 --- /dev/null +++ b/study-app-tutorials/lib/model/request_host.dart @@ -0,0 +1,7 @@ + +class RequestHost { + String protocol = ""; + String host = ""; + String port = ""; + +} \ No newline at end of file diff --git a/study-app-tutorials/lib/model/route_path.dart b/study-app-tutorials/lib/model/route_path.dart new file mode 100644 index 000000000..667e4a691 --- /dev/null +++ b/study-app-tutorials/lib/model/route_path.dart @@ -0,0 +1,22 @@ +import 'package:flutter/material.dart'; + +typedef PathWidgetBuilder = Widget Function(BuildContext, String); + +class RoutePath { + const RoutePath(this.pattern, this.builder); + + /// A RegEx string for route matching. + final String pattern; + + /// The builder for the associated pattern route. The first argument is the + /// [BuildContext] and the second argument a RegEx match if that is included + /// in the pattern. + /// + /// ```dart + /// Path( + /// 'r'^/demo/([\w-]+)$', + /// (context, matches) => Page(argument: match), + /// ) + /// ``` + final PathWidgetBuilder builder; +} diff --git a/study-app-tutorials/lib/prop/http_prop.dart b/study-app-tutorials/lib/prop/http_prop.dart new file mode 100644 index 000000000..cb57dc381 --- /dev/null +++ b/study-app-tutorials/lib/prop/http_prop.dart @@ -0,0 +1,20 @@ +import 'package:tutorials/component/cache/http_request_caches.dart'; + +class HttpProp { + static String getBaseUrl() { + return HttpRequestCaches.getProtocol() + + "://" + + HttpRequestCaches.getHost() + + ":" + + HttpRequestCaches.getPort() + + "/tutorials"; + } + + static int getTimeout() { + return 5000; + } + + static String getContentType() { + return "application/json; charset=utf-8"; + } +} diff --git a/study-app-tutorials/lib/request/feedback_request.dart b/study-app-tutorials/lib/request/feedback_request.dart new file mode 100644 index 000000000..a0b37151e --- /dev/null +++ b/study-app-tutorials/lib/request/feedback_request.dart @@ -0,0 +1,127 @@ +import 'dart:collection'; + +import 'package:tutorials/component/log/logs.dart'; +import 'package:tutorials/request/model/feedback/message_feedback.dart'; +import 'package:tutorials/request/model/feedback/message_feedback_delete_request_param.dart'; +import 'package:tutorials/request/model/feedback/message_feedback_delete_request_result.dart'; +import 'package:tutorials/request/model/feedback/message_feedback_insert_request_param.dart'; +import 'package:tutorials/request/model/feedback/message_feedback_insert_request_result.dart'; +import 'package:tutorials/request/model/feedback/message_feedback_query_request_param.dart'; +import 'package:tutorials/request/model/feedback/message_feedback_query_request_result.dart'; + +class MessageFeedbackRequests { + static List list = []; + + static Future query( + MessageFeedbackQueryRequestParam requestParam) async { + Logs.info('request param : ${requestParam?.toString()}'); + + // String url = HttpConstant.url_settings_version_check + // .replaceAll("{version}", AppUtils.getCurrentVersion().toString()); + // HttpResult httpResult = await HttpRequests.get(url, null, null); + // String? temp = httpResult?.responseBody; + // String responseBody = temp == null ? "" : temp; + // Logs.info("responseBody = ${responseBody}"); + Map param = HashMap(); + // param.putIfAbsent("param", () => userName); + // return HttpRequests.post(HttpConstant.url_login, param, null); + return Future.delayed(const Duration(seconds: 1), () => mock()); + } + + static Future insert( + MessageFeedbackInsertRequestParam requestParam) async { + Logs.info('request param : ${requestParam?.toString()}'); + + // String url = HttpConstant.url_settings_version_check + // .replaceAll("{version}", AppUtils.getCurrentVersion().toString()); + // HttpResult httpResult = await HttpRequests.get(url, null, null); + // String? temp = httpResult?.responseBody; + // String responseBody = temp == null ? "" : temp; + // Logs.info("responseBody = ${responseBody}"); + Map param = HashMap(); + // param.putIfAbsent("param", () => userName); + // return HttpRequests.post(HttpConstant.url_login, param, null); + return Future.delayed( + const Duration(seconds: 1), () => mock2(requestParam.feedback)); + } + + static Future delete( + MessageFeedbackDeleteRequestParam requestParam) async { + Logs.info('request param : ${requestParam?.toString()}'); + + // String url = HttpConstant.url_settings_version_check + // .replaceAll("{version}", AppUtils.getCurrentVersion().toString()); + // HttpResult httpResult = await HttpRequests.get(url, null, null); + // String? temp = httpResult?.responseBody; + // String responseBody = temp == null ? "" : temp; + // Logs.info("responseBody = ${responseBody}"); + Map param = HashMap(); + // param.putIfAbsent("param", () => userName); + // return HttpRequests.post(HttpConstant.url_login, param, null); + return Future.delayed( + const Duration(seconds: 1), () => mock3(requestParam.feedbackId)); + } + + static MessageFeedbackDeleteRequestResult mock3(int? feedbackId) { + if (feedbackId != null) { + int index = list.indexWhere((element) => element.id == feedbackId); + Logs.info('delete item: $list[index].toString()'); + list.removeAt(index); + } + + MessageFeedbackDeleteRequestResult result = + MessageFeedbackDeleteRequestResult(); + result.common.code = 200; + result.common.message = "ok"; + Logs.info('request result : ${result?.toString()}'); + return result; + } + + static MessageFeedbackInsertRequestResult mock2(MessageFeedback? feedback) { + if (feedback != null) { + feedback.id = DateTime.now().microsecond; + list.add(feedback); + } + + MessageFeedbackInsertRequestResult result = + MessageFeedbackInsertRequestResult(); + result.common.code = 200; + result.common.message = "ok"; + Logs.info('request result : ${result?.toString()}'); + return result; + } + + static MessageFeedbackQueryRequestResult mock() { + if (list.isEmpty) { + init(); + } + + MessageFeedbackQueryRequestResult result = + MessageFeedbackQueryRequestResult(); + result.common.code = 200; + result.common.message = "ok"; + result.feedbacks = list; + + Logs.info('request result : ${result?.toString()}'); + return result; + } + + static void init() { + MessageFeedback feedback = MessageFeedback(); + feedback.id = DateTime.now().microsecond; + feedback.fromUserId = 1235; + feedback.fromUserName = '张三'; + feedback.fromUserIconUrl = + 'https://avatars.githubusercontent.com/u/18094768?v=4'; + feedback.toUserId = 1234; + feedback.toUserName = '李四'; + feedback.msgContent = '这是留言内容,吧啦吧啦吧啦吧'; + feedback.readStatus = 'read'; + feedback.sendTime = '2022-06-06 10:24'; + feedback.readTime = '2022-06-06 10:24'; + feedback.msgType = 'default'; + feedback.createTime = '2022-06-06 10:24'; + + list.add(feedback); + } +} diff --git a/study-app-tutorials/lib/request/file_upload_request.dart b/study-app-tutorials/lib/request/file_upload_request.dart new file mode 100644 index 000000000..927dbab0b --- /dev/null +++ b/study-app-tutorials/lib/request/file_upload_request.dart @@ -0,0 +1,35 @@ +import 'dart:collection'; + +import 'package:dio/src/multipart_file.dart'; +import 'package:tutorials/component/log/logs.dart'; +import 'package:tutorials/request/model/setting/app_version_check_request_param.dart'; +import 'package:tutorials/request/model/setting/app_version_check_request_result.dart'; +import 'package:tutorials/request/model/upload/file_upload_param.dart'; +import 'package:tutorials/request/model/upload/file_upload_result.dart'; + +class FileUploadRequests { + static Future upload(FileUploadParam param, Function progress){ + // Logs.info('request param : ${param?.toString()}'); + + // String url = HttpConstant.url_settings_version_check + // .replaceAll("{version}", AppUtils.getCurrentVersion().toString()); + // HttpResult httpResult = await HttpRequests.get(url, null, null); + // String? temp = httpResult?.responseBody; + // String responseBody = temp == null ? "" : temp; + // Logs.info("responseBody = ${responseBody}"); + Map param = HashMap(); + // param.putIfAbsent("param", () => userName); + // return HttpRequests.post(HttpConstant.url_login, param, null); + return Future.delayed(const Duration(seconds: 1), () => mock()); + } + + static FileUploadResult mock() { + FileUploadResult result = FileUploadResult(); + result.common.code = 200; + // result.common.code = ErrorCodeConstant.loginSecurityCodeRequired; + result.common.message = "404啦啦啦"; + Logs.info('request result : ${result?.toString()}'); + return result; + } + +} diff --git a/study-app-tutorials/lib/request/forget_password_requests.dart b/study-app-tutorials/lib/request/forget_password_requests.dart new file mode 100644 index 000000000..c6fb33ef3 --- /dev/null +++ b/study-app-tutorials/lib/request/forget_password_requests.dart @@ -0,0 +1,24 @@ +import 'dart:collection'; + +import 'package:tutorials/component/log/logs.dart'; +import 'package:tutorials/request/model/forget/forget_password_request_param.dart'; +import 'package:tutorials/request/model/forget/forget_password_request_result.dart'; + +class ForgetPasswordRequests { + static Future reset( + ForgetPasswordRequestParam requestParam) async { + Logs.info('request param : ${requestParam?.toString()}'); + Map param = HashMap(); + // param.putIfAbsent("param", () => userName); + // return HttpRequests.post(HttpConstant.url_login, param, null); + return Future.delayed(const Duration(seconds: 1), () => mock()); + } + + static ForgetPasswordRequestResult mock() { + ForgetPasswordRequestResult result = ForgetPasswordRequestResult(); + result.common.code = 200; + // result.common.message = "404啦啦啦"; + Logs.info('request result : ${result?.toString()}'); + return result; + } +} diff --git a/study-app-tutorials/lib/request/login_requests.dart b/study-app-tutorials/lib/request/login_requests.dart new file mode 100644 index 000000000..eab2425d8 --- /dev/null +++ b/study-app-tutorials/lib/request/login_requests.dart @@ -0,0 +1,97 @@ +import 'dart:collection'; +import 'dart:convert'; + +import 'package:tutorials/component/cache/setting_caches.dart'; +import 'package:tutorials/component/cache/token_caches.dart'; +import 'package:tutorials/component/http/http_requests.dart'; +import 'package:tutorials/component/http/http_result.dart'; +import 'package:tutorials/component/log/logs.dart'; +import 'package:tutorials/constant/http_constant.dart'; +import 'package:tutorials/constant/sp_constant.dart'; +import 'package:tutorials/request/model/login/login_request_param.dart'; +import 'package:tutorials/request/model/login/login_request_result.dart'; +import 'package:tutorials/request/origin/auth_origin_result.dart'; +import 'package:tutorials/request/origin/login_origin_result.dart'; +import 'package:tutorials/request/origin/login_origin_result_mapping.dart'; +import 'package:tutorials/utils/crypt_utils.dart'; + +class LoginRequests { + + static Future tryLogin( + String token) async { + Logs.info('request param : ${token}'); + + Map param = HashMap(); + Map header = HashMap(); + header.putIfAbsent( + "Authorization", () => "Bearer ${token}"); + Logs.info('request header : ${header?.toString()}'); + + return Future.value( + HttpRequests.post(HttpConstant.url_user_profile, param, header) + .then((value) => LoginOriginResultMapping.mapping(value))); + + } + + static Future _auth(LoginRequestParam requestParam) async { + Logs.info('request param : ${requestParam?.toString()}'); + Map param = HashMap(); + param.putIfAbsent("grant_type", () => 'password'); + param.putIfAbsent("username", () => requestParam?.userName ?? ''); + param.putIfAbsent("password", () => requestParam?.password ?? ''); + + Map header = HashMap(); + String userAndPass = "client:secret"; + header.putIfAbsent( + "Authorization", () => "Basic " + CryptUtils.encode(userAndPass)); + + return Future.value( + HttpRequests.post(HttpConstant.url_login, param, header).then((value) { + return AuthOriginResult.fromJson(jsonDecode(value.responseBody)); + })); + + } + + static Future login( + LoginRequestParam requestParam) async { + Logs.info('request param : ${requestParam?.toString()}'); + if(await SettingCaches.getMockSwitch() == 'true'){ + return Future.delayed(const Duration(seconds: 1), () => mock()); + } + AuthOriginResult auth = await _auth(requestParam); + Logs.info('request auth : ${auth.toJson()}'); + if(auth?.code != 200){ + LoginRequestResult result = LoginRequestResult(); + result.common.code = auth.code??0; + result.common.message = auth.msg??''; + return result; + } + + TokenCaches.cacheAccessToken(auth?.data?.accessToken??''); + TokenCaches.cacheRefreshToken(auth?.data?.refreshToken??''); + + Map param = HashMap(); + Map header = HashMap(); + header.putIfAbsent( + "Authorization", () => "Bearer ${auth?.data?.accessToken ?? ''}"); + Logs.info('request header : ${header?.toString()}'); + + return Future.value( + HttpRequests.post(HttpConstant.url_user_profile, param, header) + .then((value) => LoginOriginResultMapping.mapping(value))); + + } + + static LoginRequestResult mock() { + LoginRequestResult result = LoginRequestResult(); + result.common.code = 200; + // result.common.code = ErrorCodeConstant.loginSecurityCodeRequired; + result.common.message = "404啦啦啦"; + result.id = 12345; + result.userName = '小陆[已登陆]'; + result.mail = 'bage@qq.com'; + result.iconUrl = 'https://avatars.githubusercontent.com/u/18094768?v=4'; + Logs.info('mock request result : ${result?.toString()}'); + return result; + } +} diff --git a/study-app-tutorials/lib/request/model/AppVersionResult.dart b/study-app-tutorials/lib/request/model/AppVersionResult.dart new file mode 100644 index 000000000..18f5e0879 --- /dev/null +++ b/study-app-tutorials/lib/request/model/AppVersionResult.dart @@ -0,0 +1,32 @@ +import '../../model/app_version.dart'; + +class AppVersionResult { + int? code; + int? originCode; + String? msg; + Null? originMsg; + AppVersion? data; + + AppVersionResult( + {this.code, this.originCode, this.msg, this.originMsg, this.data}); + + AppVersionResult.fromJson(Map json) { + code = json['code']; + originCode = json['originCode']; + msg = json['msg']; + originMsg = json['originMsg']; + data = json['data'] != null ? new AppVersion.fromJson(json['data']) : null; + } + + Map toJson() { + final Map data = new Map(); + data['code'] = this.code; + data['originCode'] = this.originCode; + data['msg'] = this.msg; + data['originMsg'] = this.originMsg; + if (this.data != null) { + data['data'] = this.data?.toJson(); + } + return data; + } +} diff --git a/study-app-tutorials/lib/request/model/AppVersionsResult.dart b/study-app-tutorials/lib/request/model/AppVersionsResult.dart new file mode 100644 index 000000000..a50185488 --- /dev/null +++ b/study-app-tutorials/lib/request/model/AppVersionsResult.dart @@ -0,0 +1,71 @@ +import '../../model/app_version.dart'; + +class AppVersionsResult { + int? code; + int? originCode; + String? msg; + String? originMsg; + List? data; + Pagination? pagination; + + AppVersionsResult( + {this.code, + this.originCode, + this.msg, + this.originMsg, + this.data, + this.pagination}); + + AppVersionsResult.fromJson(Map json) { + code = json['code']; + originCode = json['originCode']; + msg = json['msg']; + originMsg = json['originMsg']; + if (json['data'] != null) { + data = []; + json['data'].forEach((v) { + data?.add(new AppVersion.fromJson(v)); + }); + } + pagination = json['pagination'] != null + ? new Pagination.fromJson(json['pagination']) + : null; + } + + Map toJson() { + final Map data = new Map(); + data['code'] = this.code; + data['originCode'] = this.originCode; + data['msg'] = this.msg; + data['originMsg'] = this.originMsg; + if (this.data != null) { + data['data'] = this.data?.map((v) => v.toJson()).toList(); + } + if (this.pagination != null) { + data['pagination'] = this.pagination?.toJson(); + } + return data; + } +} + +class Pagination { + int? targetPage; + int? pageSize; + int? total; + + Pagination({this.targetPage, this.pageSize, this.total}); + + Pagination.fromJson(Map json) { + targetPage = json['targetPage']; + pageSize = json['pageSize']; + total = json['total']; + } + + Map toJson() { + final Map data = new Map(); + data['targetPage'] = this.targetPage; + data['pageSize'] = this.pageSize; + data['total'] = this.total; + return data; + } +} diff --git a/study-app-tutorials/lib/request/model/AuthorInfo.dart b/study-app-tutorials/lib/request/model/AuthorInfo.dart new file mode 100644 index 000000000..21fb80040 --- /dev/null +++ b/study-app-tutorials/lib/request/model/AuthorInfo.dart @@ -0,0 +1,11 @@ + +class AuthorInfo { + int? id; + String? firstName; + String? lastName; + String? iconUrl; + String? homePageUrl; + String? description; + String? email; + +} diff --git a/study-app-tutorials/lib/request/model/FeedbackQueryResult.dart b/study-app-tutorials/lib/request/model/FeedbackQueryResult.dart new file mode 100644 index 000000000..acd0fc6ac --- /dev/null +++ b/study-app-tutorials/lib/request/model/FeedbackQueryResult.dart @@ -0,0 +1,52 @@ +import '../../model/app_feedback.dart'; +import '../../model/pagination.dart'; + +class FeedbackQueryResult { + int? code; + int? originCode; + String? msg; + String? originMsg; + List? data; + Pagination? pagination; + + FeedbackQueryResult( + {this.code, + this.originCode, + this.msg, + this.originMsg, + this.data, + this.pagination}); + + FeedbackQueryResult.fromJson(Map json) { + code = json['code']; + originCode = json['originCode']; + msg = json['msg']; + originMsg = json['originMsg']; + if (json['data'] != null) { + data = []; + json['data'].forEach((v) { + data?.add(new AppFeedback.fromJson(v)); + }); + } + pagination = json['pagination'] != null + ? new Pagination.fromJson(json['pagination']) + : null; + } + + Map toJson() { + final Map data = new Map(); + data['code'] = this.code; + data['originCode'] = this.originCode; + data['msg'] = this.msg; + data['originMsg'] = this.originMsg; + if (this.data != null) { + data['data'] = this.data?.map((v) => v.toJson()).toList(); + } + if (this.pagination != null) { + data['pagination'] = this.pagination?.toJson(); + } + return data; + } +} + + diff --git a/study-app-tutorials/lib/request/model/common/common_param.dart b/study-app-tutorials/lib/request/model/common/common_param.dart new file mode 100644 index 000000000..fbbed5033 --- /dev/null +++ b/study-app-tutorials/lib/request/model/common/common_param.dart @@ -0,0 +1,25 @@ +class CommonParam { + CommonParam(); + String? traceId; + String? deviceId; + String? version; + + CommonParam.fromJson(Map json){ + traceId = json['traceId']; + deviceId = json['deviceId']; + version = json['version']; + } + + Map toJson() { + final _data = {}; + _data['traceId'] = traceId; + _data['deviceId'] = deviceId; + _data['version'] = version; + return _data; + } + + @override + String toString() { + return 'CommonParam{traceId: $traceId, deviceId: $deviceId, version: $version}'; + } +} \ No newline at end of file diff --git a/study-app-tutorials/lib/request/model/common/common_result.dart b/study-app-tutorials/lib/request/model/common/common_result.dart new file mode 100644 index 000000000..50fa43ca1 --- /dev/null +++ b/study-app-tutorials/lib/request/model/common/common_result.dart @@ -0,0 +1,11 @@ + +class CommonResult { + + int code = 0; + String message = ''; + + @override + String toString() { + return 'CommonResult{code: $code, message: $message}'; + } +} \ No newline at end of file diff --git a/study-app-tutorials/lib/request/model/common/page_query_param.dart b/study-app-tutorials/lib/request/model/common/page_query_param.dart new file mode 100644 index 000000000..ea4bf7de6 --- /dev/null +++ b/study-app-tutorials/lib/request/model/common/page_query_param.dart @@ -0,0 +1,23 @@ +class PageQueryParam { + PageQueryParam(); + int targetPage = 1; + int pageSize = 10; + + + PageQueryParam.fromJson(Map json){ + targetPage = json['targetPage']; + pageSize = json['pageSize']; + } + + Map toJson() { + final _data = {}; + _data['targetPage'] = targetPage; + _data['pageSize'] = pageSize; + return _data; + } + + @override + String toString() { + return 'PageQueryParam{targetPage: $targetPage, pageSize: $pageSize'; + } +} \ No newline at end of file diff --git a/study-app-tutorials/lib/request/model/feedback/message_feedback.dart b/study-app-tutorials/lib/request/model/feedback/message_feedback.dart new file mode 100644 index 000000000..cda3b83cc --- /dev/null +++ b/study-app-tutorials/lib/request/model/feedback/message_feedback.dart @@ -0,0 +1,16 @@ +class MessageFeedback{ + int? id; + int? fromUserId; + String? fromUserName; + String? fromUserIconUrl; + int? toUserId; + String? toUserName; + String? msgContent; + String? readStatus; + String? sendTime; + String? readTime; + String? msgType; + String? createTime; + + +} \ No newline at end of file diff --git a/study-app-tutorials/lib/request/model/feedback/message_feedback_delete_request_param.dart b/study-app-tutorials/lib/request/model/feedback/message_feedback_delete_request_param.dart new file mode 100644 index 000000000..29d3f1b06 --- /dev/null +++ b/study-app-tutorials/lib/request/model/feedback/message_feedback_delete_request_param.dart @@ -0,0 +1,14 @@ +import 'package:tutorials/request/model/common/common_param.dart'; +import 'package:tutorials/request/model/common/page_query_param.dart'; +import 'package:tutorials/request/model/feedback/message_feedback.dart'; + +class MessageFeedbackDeleteRequestParam { + CommonParam common = CommonParam(); + + int? feedbackId; + + @override + String toString() { + return 'MessageFeedbackQueryRequestParam{common: $common, feedbackId: $feedbackId}'; + } +} diff --git a/study-app-tutorials/lib/request/model/feedback/message_feedback_delete_request_result.dart b/study-app-tutorials/lib/request/model/feedback/message_feedback_delete_request_result.dart new file mode 100644 index 000000000..21349c311 --- /dev/null +++ b/study-app-tutorials/lib/request/model/feedback/message_feedback_delete_request_result.dart @@ -0,0 +1,6 @@ +import 'package:tutorials/request/model/common/common_result.dart'; +import 'package:tutorials/request/model/feedback/message_feedback.dart'; + +class MessageFeedbackDeleteRequestResult { + CommonResult common = CommonResult(); +} diff --git a/study-app-tutorials/lib/request/model/feedback/message_feedback_insert_request_param.dart b/study-app-tutorials/lib/request/model/feedback/message_feedback_insert_request_param.dart new file mode 100644 index 000000000..6c1561248 --- /dev/null +++ b/study-app-tutorials/lib/request/model/feedback/message_feedback_insert_request_param.dart @@ -0,0 +1,14 @@ +import 'package:tutorials/request/model/common/common_param.dart'; +import 'package:tutorials/request/model/common/page_query_param.dart'; +import 'package:tutorials/request/model/feedback/message_feedback.dart'; + +class MessageFeedbackInsertRequestParam { + CommonParam common = CommonParam(); + + MessageFeedback? feedback; + + @override + String toString() { + return 'MessageFeedbackQueryRequestParam{common: $common, feedback: $feedback}'; + } +} diff --git a/study-app-tutorials/lib/request/model/feedback/message_feedback_insert_request_result.dart b/study-app-tutorials/lib/request/model/feedback/message_feedback_insert_request_result.dart new file mode 100644 index 000000000..887d537e7 --- /dev/null +++ b/study-app-tutorials/lib/request/model/feedback/message_feedback_insert_request_result.dart @@ -0,0 +1,6 @@ +import 'package:tutorials/request/model/common/common_result.dart'; +import 'package:tutorials/request/model/feedback/message_feedback.dart'; + +class MessageFeedbackInsertRequestResult { + CommonResult common = CommonResult(); +} diff --git a/study-app-tutorials/lib/request/model/feedback/message_feedback_query_request_param.dart b/study-app-tutorials/lib/request/model/feedback/message_feedback_query_request_param.dart new file mode 100644 index 000000000..5e77a319b --- /dev/null +++ b/study-app-tutorials/lib/request/model/feedback/message_feedback_query_request_param.dart @@ -0,0 +1,15 @@ +import 'package:tutorials/request/model/common/common_param.dart'; +import 'package:tutorials/request/model/common/page_query_param.dart'; + +class MessageFeedbackQueryRequestParam { + CommonParam common = CommonParam(); + PageQueryParam page = PageQueryParam(); + + String? keyword; + int? fromUserId; + + @override + String toString() { + return 'FeedbackQueryRequestParam{common: $common, page: $page, keyword: $keyword, fromUserId: $fromUserId}'; + } +} diff --git a/study-app-tutorials/lib/request/model/feedback/message_feedback_query_request_result.dart b/study-app-tutorials/lib/request/model/feedback/message_feedback_query_request_result.dart new file mode 100644 index 000000000..c95b2f00b --- /dev/null +++ b/study-app-tutorials/lib/request/model/feedback/message_feedback_query_request_result.dart @@ -0,0 +1,11 @@ + +import 'package:tutorials/request/model/common/common_result.dart'; +import 'package:tutorials/request/model/feedback/message_feedback.dart'; + +class MessageFeedbackQueryRequestResult { + + CommonResult common = CommonResult(); + + List feedbacks = []; + +} diff --git a/study-app-tutorials/lib/request/model/forget/forget_password_request_param.dart b/study-app-tutorials/lib/request/model/forget/forget_password_request_param.dart new file mode 100644 index 000000000..c31b8a110 --- /dev/null +++ b/study-app-tutorials/lib/request/model/forget/forget_password_request_param.dart @@ -0,0 +1,28 @@ +import 'package:tutorials/request/model/common/common_param.dart'; + +class ForgetPasswordRequestParam { + String? userName; + String? securityCode; + CommonParam common = CommonParam(); + + ForgetPasswordRequestParam(); + + ForgetPasswordRequestParam.fromJson(Map json) { + userName = json['userName']; + securityCode = json['securityCode']; + common = CommonParam.fromJson(json['common']); + } + + Map toJson() { + final _data = {}; + _data['userName'] = userName; + _data['securityCode'] = securityCode; + _data['common'] = common.toJson(); + return _data; + } + + @override + String toString() { + return 'ForgetPasswordRequestParam{userName: $userName, securityCode: $securityCode, common: $common}'; + } +} diff --git a/study-app-tutorials/lib/request/model/forget/forget_password_request_result.dart b/study-app-tutorials/lib/request/model/forget/forget_password_request_result.dart new file mode 100644 index 000000000..04a9de77b --- /dev/null +++ b/study-app-tutorials/lib/request/model/forget/forget_password_request_result.dart @@ -0,0 +1,12 @@ + +import 'package:tutorials/request/model/common/common_result.dart'; + +class ForgetPasswordRequestResult { + + CommonResult common = CommonResult(); + + @override + String toString() { + return 'ForgetPasswordRequestResult{common: $common}'; + } +} diff --git a/study-app-tutorials/lib/request/model/login/login_request_param.dart b/study-app-tutorials/lib/request/model/login/login_request_param.dart new file mode 100644 index 000000000..670e22025 --- /dev/null +++ b/study-app-tutorials/lib/request/model/login/login_request_param.dart @@ -0,0 +1,14 @@ +import 'package:tutorials/request/model/common/common_param.dart'; + +class LoginRequestParam { + String? userName; + String? password; + String? securityCode; + + CommonParam common = CommonParam(); + + @override + String toString() { + return 'LoginRequestParam{userName: $userName, password: $password, securityCode: $securityCode, common: $common}'; + } +} diff --git a/study-app-tutorials/lib/request/model/login/login_request_result.dart b/study-app-tutorials/lib/request/model/login/login_request_result.dart new file mode 100644 index 000000000..de966b69a --- /dev/null +++ b/study-app-tutorials/lib/request/model/login/login_request_result.dart @@ -0,0 +1,16 @@ +import '../common/common_result.dart'; + +class LoginRequestResult { + + CommonResult common = CommonResult(); + + int? id; + String? userName; + String? iconUrl; + String? mail; + + @override + String toString() { + return 'LoginRequestResult{common: $common, id: $id, userName: $userName, iconUrl: $iconUrl, mail: $mail}'; + } +} diff --git a/study-app-tutorials/lib/request/model/register/register_request_param.dart b/study-app-tutorials/lib/request/model/register/register_request_param.dart new file mode 100644 index 000000000..d261ef551 --- /dev/null +++ b/study-app-tutorials/lib/request/model/register/register_request_param.dart @@ -0,0 +1,31 @@ +import 'package:tutorials/request/model/common/common_param.dart'; + +class RegisterRequestParam { + String? userName; + String? password; + String? securityCode; + CommonParam common = CommonParam(); + + RegisterRequestParam(); + + RegisterRequestParam.fromJson(Map json) { + userName = json['userName']; + password = json['password']; + securityCode = json['securityCode']; + common = CommonParam.fromJson(json['common']); + } + + Map toJson() { + final _data = {}; + _data['userName'] = userName; + _data['password'] = password; + _data['securityCode'] = securityCode; + _data['common'] = common.toJson(); + return _data; + } + + @override + String toString() { + return 'RegisterRequestParam{userName: $userName, password: $password, securityCode: $securityCode, common: $common}'; + } +} diff --git a/study-app-tutorials/lib/request/model/register/register_request_result.dart b/study-app-tutorials/lib/request/model/register/register_request_result.dart new file mode 100644 index 000000000..4738d6156 --- /dev/null +++ b/study-app-tutorials/lib/request/model/register/register_request_result.dart @@ -0,0 +1,12 @@ +import '../common/common_result.dart'; + +class RegisterRequestResult { + + CommonResult common = CommonResult(); + + @override + String toString() { + return 'RegisterRequestResult{common: $common}'; + } + +} diff --git a/study-app-tutorials/lib/request/model/security_code_request_param.dart b/study-app-tutorials/lib/request/model/security_code_request_param.dart new file mode 100644 index 000000000..9f85b3d9f --- /dev/null +++ b/study-app-tutorials/lib/request/model/security_code_request_param.dart @@ -0,0 +1,13 @@ +import 'package:tutorials/request/model/common/common_param.dart'; + +class SecurityCodeRequestParam { + + CommonParam common = CommonParam(); + + String clientId = ""; + + @override + String toString() { + return 'SecurityCodeRequestParam{common: $common, clientId: $clientId}'; + } +} diff --git a/study-app-tutorials/lib/request/model/security_code_request_result.dart b/study-app-tutorials/lib/request/model/security_code_request_result.dart new file mode 100644 index 000000000..39c365a99 --- /dev/null +++ b/study-app-tutorials/lib/request/model/security_code_request_result.dart @@ -0,0 +1,14 @@ + +import 'package:tutorials/request/model/common/common_result.dart'; + +class SecurityCodeRequestResult { + + CommonResult common = CommonResult(); + + String url = ''; + + @override + String toString() { + return 'SecurityCodeRequestResult{common: $common, url: $url}'; + } +} diff --git a/study-app-tutorials/lib/request/model/setting/app_version_check_request_param.dart b/study-app-tutorials/lib/request/model/setting/app_version_check_request_param.dart new file mode 100644 index 000000000..017fbecd5 --- /dev/null +++ b/study-app-tutorials/lib/request/model/setting/app_version_check_request_param.dart @@ -0,0 +1,10 @@ +import 'package:tutorials/request/model/common/common_param.dart'; + +class AppVersionCheckRequestParam { + CommonParam common = CommonParam(); + + @override + String toString() { + return 'LoginRequestParam{common: $common}'; + } +} diff --git a/study-app-tutorials/lib/request/model/setting/app_version_check_request_result.dart b/study-app-tutorials/lib/request/model/setting/app_version_check_request_result.dart new file mode 100644 index 000000000..0ff5d975a --- /dev/null +++ b/study-app-tutorials/lib/request/model/setting/app_version_check_request_result.dart @@ -0,0 +1,17 @@ + +import 'package:tutorials/request/model/common/common_result.dart'; + +class AppVersionCheckRequestResult { + + CommonResult common = CommonResult(); + + int? id; + int? versionCode; + String? description; + int? fileId; + String? fileUrl; + String? versionName; + int? fileSize; + String? updateType; + +} diff --git a/study-app-tutorials/lib/request/model/upload/file_upload_param.dart b/study-app-tutorials/lib/request/model/upload/file_upload_param.dart new file mode 100644 index 000000000..d4d1ac82b --- /dev/null +++ b/study-app-tutorials/lib/request/model/upload/file_upload_param.dart @@ -0,0 +1,10 @@ +import 'package:dio/dio.dart'; +import 'package:tutorials/request/model/common/common_param.dart'; + +class FileUploadParam { + CommonParam common = CommonParam(); + + Map? formData; + List? files; + +} diff --git a/study-app-tutorials/lib/request/model/upload/file_upload_result.dart b/study-app-tutorials/lib/request/model/upload/file_upload_result.dart new file mode 100644 index 000000000..fcdcb00a4 --- /dev/null +++ b/study-app-tutorials/lib/request/model/upload/file_upload_result.dart @@ -0,0 +1,8 @@ + +import 'package:tutorials/request/model/common/common_result.dart'; + +class FileUploadResult { + + CommonResult common = CommonResult(); + +} diff --git a/study-app-tutorials/lib/request/model/user.dart b/study-app-tutorials/lib/request/model/user.dart new file mode 100644 index 000000000..270aa462d --- /dev/null +++ b/study-app-tutorials/lib/request/model/user.dart @@ -0,0 +1,19 @@ + +import 'package:tutorials/request/model/login/login_request_result.dart'; + +class User { + int? id; + String? userName; + String? iconUrl; + String? mail; + + static User from(LoginRequestResult result) { + User user = User(); + user.id = result.id; + user.userName = result.userName; + user.iconUrl = result.iconUrl; + user.mail = result.mail; + return user; + } + +} diff --git a/study-app-tutorials/lib/request/model/user/detail/user_detail_param.dart b/study-app-tutorials/lib/request/model/user/detail/user_detail_param.dart new file mode 100644 index 000000000..641c1afe0 --- /dev/null +++ b/study-app-tutorials/lib/request/model/user/detail/user_detail_param.dart @@ -0,0 +1,14 @@ +import 'package:tutorials/request/model/common/common_param.dart'; + +class UserDetailParam { + String? userName; + String? password; + String? securityCode; + + CommonParam common = CommonParam(); + + @override + String toString() { + return 'UserDetailParam{userName: $userName, password: $password, securityCode: $securityCode, common: $common}'; + } +} diff --git a/study-app-tutorials/lib/request/model/user/detail/user_detail_result.dart b/study-app-tutorials/lib/request/model/user/detail/user_detail_result.dart new file mode 100644 index 000000000..caa922436 --- /dev/null +++ b/study-app-tutorials/lib/request/model/user/detail/user_detail_result.dart @@ -0,0 +1,17 @@ + +import 'package:tutorials/request/model/common/common_result.dart'; + +class UserDetailResult { + + CommonResult common = CommonResult(); + + int? id; + String? userName; + String? iconUrl; + String? mail; + + @override + String toString() { + return 'UserDetailResult{common: $common, id: $id, userName: $userName, iconUrl: $iconUrl, mail: $mail}'; + } +} diff --git a/study-app-tutorials/lib/request/origin/auth_origin_result.dart b/study-app-tutorials/lib/request/origin/auth_origin_result.dart new file mode 100644 index 000000000..0fd14a0e4 --- /dev/null +++ b/study-app-tutorials/lib/request/origin/auth_origin_result.dart @@ -0,0 +1,71 @@ +class AuthOriginResult { + int? code; + int? originCode; + String? msg; + String? originMsg; + Data? data; + + AuthOriginResult( + {this.code, this.originCode, this.msg, this.originMsg, this.data}); + + AuthOriginResult.fromJson(Map json) { + code = json['code']; + originCode = json['originCode']; + msg = json['msg']; + originMsg = json['originMsg']; + data = json['data'] != null ? new Data.fromJson(json['data']) : null; + } + + Map toJson() { + final Map data = new Map(); + data['code'] = this.code; + data['originCode'] = this.originCode; + data['msg'] = this.msg; + data['originMsg'] = this.originMsg; + if (this.data != null) { + data['data'] = this.data!.toJson(); + } + return data; + } +} + +class Data { + String? accessToken; + String? tokenType; + String? refreshToken; + int? expiresIn; + String? scope; + String? sub; + String? jti; + + Data( + {this.accessToken, + this.tokenType, + this.refreshToken, + this.expiresIn, + this.scope, + this.sub, + this.jti}); + + Data.fromJson(Map json) { + accessToken = json['access_token']; + tokenType = json['token_type']; + refreshToken = json['refresh_token']; + expiresIn = json['expires_in']; + scope = json['scope']; + sub = json['sub']; + jti = json['jti']; + } + + Map toJson() { + final Map data = new Map(); + data['access_token'] = this.accessToken; + data['token_type'] = this.tokenType; + data['refresh_token'] = this.refreshToken; + data['expires_in'] = this.expiresIn; + data['scope'] = this.scope; + data['sub'] = this.sub; + data['jti'] = this.jti; + return data; + } +} diff --git a/study-app-tutorials/lib/request/origin/auth_origin_result_mapping.dart b/study-app-tutorials/lib/request/origin/auth_origin_result_mapping.dart new file mode 100644 index 000000000..59d09b036 --- /dev/null +++ b/study-app-tutorials/lib/request/origin/auth_origin_result_mapping.dart @@ -0,0 +1,19 @@ +import 'dart:convert'; + +import 'package:tutorials/component/http/http_result.dart'; +import 'package:tutorials/component/log/logs.dart'; +import 'package:tutorials/request/model/login/login_request_result.dart'; +import 'package:tutorials/request/origin/auth_origin_result.dart'; +import 'package:tutorials/request/origin/login_origin_result.dart'; + +class AuthOriginResultMapping { + + static AuthOriginResult mapping(HttpResult httpResult) { + Logs.info('httpResult ${httpResult.responseBody}'); + + AuthOriginResult originResult = AuthOriginResult.fromJson(jsonDecode(httpResult.responseBody)); + return originResult; + + } + +} diff --git a/study-app-tutorials/lib/request/origin/login_origin_result.dart b/study-app-tutorials/lib/request/origin/login_origin_result.dart new file mode 100644 index 000000000..8383297a0 --- /dev/null +++ b/study-app-tutorials/lib/request/origin/login_origin_result.dart @@ -0,0 +1,91 @@ +class LoginOriginResult { + int? code; + int? originCode; + String? msg; + String? originMsg; + Data? data; + + LoginOriginResult( + {this.code, this.originCode, this.msg, this.originMsg, this.data}); + + LoginOriginResult.fromJson(Map json) { + code = json['code']; + originCode = json['originCode']; + msg = json['msg']; + originMsg = json['originMsg']; + data = json['data'] != null ? new Data.fromJson(json['data']) : null; + } + + Map toJson() { + final Map data = new Map(); + data['code'] = this.code; + data['originCode'] = this.originCode; + data['msg'] = this.msg; + data['originMsg'] = this.originMsg; + if (this.data != null) { + data['data'] = this.data!.toJson(); + } + return data; + } +} + +class Data { + int? id; + String? userName; + String? phone; + String? mail; + String? password; + String? sex; + Null? iconId; + Null? iconUrl; + String? birthday; + String? createTime; + String? updateTime; + Null? deleteState; + + Data( + {this.id, + this.userName, + this.phone, + this.mail, + this.password, + this.sex, + this.iconId, + this.iconUrl, + this.birthday, + this.createTime, + this.updateTime, + this.deleteState}); + + Data.fromJson(Map json) { + id = json['id']; + userName = json['userName']; + phone = json['phone']; + mail = json['mail']; + password = json['password']; + sex = json['sex']; + iconId = json['iconId']; + iconUrl = json['iconUrl']; + birthday = json['birthday']; + createTime = json['createTime']; + updateTime = json['updateTime']; + deleteState = json['deleteState']; + } + + Map toJson() { + final Map data = new Map(); + data['id'] = this.id; + data['userName'] = this.userName; + data['phone'] = this.phone; + data['mail'] = this.mail; + data['password'] = this.password; + data['sex'] = this.sex; + data['iconId'] = this.iconId; + data['iconUrl'] = this.iconUrl; + data['birthday'] = this.birthday; + data['createTime'] = this.createTime; + data['updateTime'] = this.updateTime; + data['deleteState'] = this.deleteState; + return data; + } +} diff --git a/study-app-tutorials/lib/request/origin/login_origin_result_mapping.dart b/study-app-tutorials/lib/request/origin/login_origin_result_mapping.dart new file mode 100644 index 000000000..82957cda0 --- /dev/null +++ b/study-app-tutorials/lib/request/origin/login_origin_result_mapping.dart @@ -0,0 +1,27 @@ +import 'dart:convert'; + +import 'package:tutorials/component/http/http_result.dart'; +import 'package:tutorials/component/log/logs.dart'; +import 'package:tutorials/request/model/login/login_request_result.dart'; +import 'package:tutorials/request/origin/login_origin_result.dart'; + +class LoginOriginResultMapping { + + static LoginRequestResult mapping(HttpResult httpResult) { + Logs.info('httpResult ${httpResult.responseBody}'); + + LoginOriginResult originResult = LoginOriginResult.fromJson(jsonDecode(httpResult.responseBody)); + + LoginRequestResult result = LoginRequestResult(); + result.common.code = originResult.code??0; + result.common.message = originResult.msg??''; + + result.id = originResult.data?.id??0; + result.userName = originResult.data?.userName??''; + result.iconUrl = originResult.data?.iconUrl??''; + result.mail = originResult.data?.mail??''; + return result; + + } + +} diff --git a/study-app-tutorials/lib/request/register_requests.dart b/study-app-tutorials/lib/request/register_requests.dart new file mode 100644 index 000000000..992cc865f --- /dev/null +++ b/study-app-tutorials/lib/request/register_requests.dart @@ -0,0 +1,43 @@ +import 'dart:collection'; + +import 'package:tutorials/component/http/http_requests.dart'; +import 'package:tutorials/component/log/logs.dart'; +import 'package:tutorials/constant/http_constant.dart'; +import 'package:tutorials/request/model/register/register_request_param.dart'; +import 'package:tutorials/request/model/register/register_request_result.dart'; + +class RegisterRequests { + static Future register( + RegisterRequestParam requestParam) async { + Logs.info('request param : ${requestParam?.toString()}'); + Map param = HashMap(); + param.putIfAbsent("username", () => requestParam?.userName ?? ''); + param.putIfAbsent("password", () => requestParam?.password ?? ''); + + Map header = HashMap(); + + // return Future.value( + // HttpRequests.post(HttpConstant.url_login, param, header).then((value) { + // return RegisterRequestResult.fromJson(jsonDecode(value.responseBody)); + // })); + + return Future.delayed(const Duration(seconds: 1), () => mock()); + } + + static Future validSecurityCode( + RegisterRequestParam requestParam) async { + Logs.info('request param : ${requestParam?.toString()}'); + Map param = HashMap(); + // param.putIfAbsent("param", () => userName); + // return HttpRequests.post(HttpConstant.url_login, param, null); + return Future.delayed(const Duration(seconds: 1), () => mock()); + } + + static RegisterRequestResult mock() { + RegisterRequestResult result = RegisterRequestResult(); + result.common.code = 200; + // result.common.message = "404啦啦啦"; + Logs.info('request result : ${result?.toString()}'); + return result; + } +} diff --git a/study-app-tutorials/lib/request/security_code_requests.dart b/study-app-tutorials/lib/request/security_code_requests.dart new file mode 100644 index 000000000..849359b92 --- /dev/null +++ b/study-app-tutorials/lib/request/security_code_requests.dart @@ -0,0 +1,36 @@ + + +import 'dart:collection'; +import 'package:tutorials/component/log/logs.dart'; +import 'package:tutorials/constant/http_constant.dart'; +import 'package:tutorials/request/model/security_code_request_param.dart'; +import 'package:tutorials/request/model/security_code_request_result.dart'; +import 'package:tutorials/utils/app_utils.dart'; + + +class SecurityCodeRequests { + + static String url(SecurityCodeRequestParam requestParam) { + Logs.info('request param : ${requestParam?.toString()}'); + // return HttpConstant.url_validate_code + '?clientId=123456'; + return 'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fpic.616pic.com%2Fys_bnew_img%2F00%2F29%2F79%2F9UMK4fzdwr.jpg&refer=http%3A%2F%2Fpic.616pic.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1665674476&t=9eb76ad34a4addda1b09213e625ae99c'; + } + + static Future load(SecurityCodeRequestParam requestParam) async { + Logs.info('request param : ${requestParam?.toString()}'); + Map param = HashMap(); + // param.putIfAbsent("param", () => userName); + // return HttpRequests.post(HttpConstant.url_login, param, null); + return Future.delayed(const Duration(seconds: 1),() => mock()); + } + + static SecurityCodeRequestResult mock(){ + SecurityCodeRequestResult result = SecurityCodeRequestResult(); + result.common.code = 200; + // result.message = "404啦啦啦"; + result.url = 'https://image.baidu.com/search/detail?ct=503316480&z=0&ipn=d&word=%E5%9B%BE%E7%89%87%20%E9%AA%8C%E8%AF%81%E7%A0%81&step_word=&hs=0&pn=3&spn=0&di=7077204560107798529&pi=0&rn=1&tn=baiduimagedetail&is=0%2C0&istype=0&ie=utf-8&oe=utf-8&in=&cl=2&lm=-1&st=undefined&cs=3617651942%2C1398176784&os=2089719489%2C3083133450&simid=3404834121%2C19490762&adpicid=0&lpn=0&ln=1767&fr=&fmq=1655655586039_R&fm=&ic=undefined&s=undefined&hd=undefined&latest=undefined©right=undefined&se=&sme=&tab=0&width=undefined&height=undefined&face=undefined&ist=&jit=&cg=&bdtype=0&oriquery=&objurl=https%3A%2F%2Fgimg2.baidu.com%2Fimage_search%2Fsrc%3Dhttp%3A%2F%2Fpic.616pic.com%2Fys_bnew_img%2F00%2F29%2F79%2F9UMK4fzdwr.jpg%26refer%3Dhttp%3A%2F%2Fpic.616pic.com%26app%3D2002%26size%3Df9999%2C10000%26q%3Da80%26n%3D0%26g%3D0n%26fmt%3Dauto%3Fsec%3D1658247603%26t%3Dcc4a8e5405c8fe5d28354a69412930b3&fromurl=ippr_z2C%24qAzdH3FAzdH3Fm8mrtv_z%26e3Bv54AzdH3Ff7vwtAzdH3Fzd6tshd2e_z%26e3Bip4s&gsm=4&rpstart=0&rpnum=0&islist=&querylist=&nojc=undefined&dyTabStr=MCwzLDUsMSw2LDQsNyw4LDIsOQ%3D%3D'; + Logs.info('request result : ${result?.toString()}'); + return result; + } + +} diff --git a/study-app-tutorials/lib/request/setting_request.dart b/study-app-tutorials/lib/request/setting_request.dart new file mode 100644 index 000000000..1a1f50c88 --- /dev/null +++ b/study-app-tutorials/lib/request/setting_request.dart @@ -0,0 +1,44 @@ +import 'dart:collection'; + +import 'package:tutorials/component/log/logs.dart'; +import 'package:tutorials/request/model/setting/app_version_check_request_param.dart'; +import 'package:tutorials/request/model/setting/app_version_check_request_result.dart'; + +class SettingRequests { + static Future checkVersion( + AppVersionCheckRequestParam requestParam) async { + Logs.info('request param : ${requestParam?.toString()}'); + + // String url = HttpConstant.url_settings_version_check + // .replaceAll("{version}", AppUtils.getCurrentVersion().toString()); + // HttpResult httpResult = await HttpRequests.get(url, null, null); + // String? temp = httpResult?.responseBody; + // String responseBody = temp == null ? "" : temp; + // Logs.info("responseBody = ${responseBody}"); + Map param = HashMap(); + // param.putIfAbsent("param", () => userName); + // return HttpRequests.post(HttpConstant.url_login, param, null); + return Future.delayed(const Duration(seconds: 1), () => mock()); + } + + static AppVersionCheckRequestResult mock() { + AppVersionCheckRequestResult result = AppVersionCheckRequestResult(); + result.common.code = 200; + // result.common.code = ErrorCodeConstant.loginSecurityCodeRequired; + result.common.message = "404啦啦啦"; + result.id = 12345; + result.versionCode = 2; + result.description = '版本信息\n' + '说明1\n' + '说明2\n' + '哈哈哈哈哈哈哈'; + result.fileId = 12345; + result.fileUrl = + "https://raw.githubusercontent.com/bage2014/study/master/study-app-tutorials/apks/app-release.apk"; + result.versionName = "Beta"; + result.fileSize = 123; + result.updateType = "Default"; + Logs.info('request result : ${result?.toString()}'); + return result; + } +} diff --git a/study-app-tutorials/lib/request/user_detail_requests.dart b/study-app-tutorials/lib/request/user_detail_requests.dart new file mode 100644 index 000000000..2cd4ec307 --- /dev/null +++ b/study-app-tutorials/lib/request/user_detail_requests.dart @@ -0,0 +1,44 @@ +import 'dart:collection'; + +import 'package:tutorials/component/http/http_requests.dart'; +import 'package:tutorials/component/http/http_result.dart'; +import 'package:tutorials/component/log/logs.dart'; +import 'package:tutorials/constant/http_constant.dart'; +import 'package:tutorials/request/model/login/login_request_param.dart'; +import 'package:tutorials/request/model/login/login_request_result.dart'; +import 'package:tutorials/request/origin/login_origin_result_mapping.dart'; +import 'package:tutorials/utils/crypt_utils.dart'; + +class LoginRequests { + static Future login( + LoginRequestParam requestParam) async { + Logs.info('request param : ${requestParam?.toString()}'); + Map param = HashMap(); + param.putIfAbsent("grant_type", () => 'password'); + param.putIfAbsent("username", () => requestParam?.userName ?? ''); + param.putIfAbsent("password", () => requestParam?.password ?? ''); + + Map header = HashMap(); + String userAndPass = "client:secret"; + header.putIfAbsent( + "Authorization", () => "Basic " + CryptUtils.encode(userAndPass)); + + return Future.value(HttpRequests.post(HttpConstant.url_login, param, header) + .then((value) => LoginOriginResultMapping.mapping(value))); + + // return Future.delayed(const Duration(seconds: 1), () => mock()); + } + + static LoginRequestResult mock() { + LoginRequestResult result = LoginRequestResult(); + result.common.code = 200; + // result.common.code = ErrorCodeConstant.loginSecurityCodeRequired; + result.common.message = "404啦啦啦"; + result.id = 12345; + result.userName = '小陆[已登陆]'; + result.mail = 'bage@qq.com'; + result.iconUrl = 'https://avatars.githubusercontent.com/u/18094768?v=4'; + Logs.info('request result : ${result?.toString()}'); + return result; + } +} diff --git a/study-app-tutorials/lib/route/route.dart b/study-app-tutorials/lib/route/route.dart new file mode 100644 index 000000000..a313270db --- /dev/null +++ b/study-app-tutorials/lib/route/route.dart @@ -0,0 +1,147 @@ +import 'package:tutorials/component/log/logs.dart'; +import 'package:tutorials/view/home/statistics.dart'; +import 'package:tutorials/view/login/forget_password_finish.dart'; +import 'package:tutorials/view/login/login.dart'; +import 'package:tutorials/view/about/about_versions.dart'; +import 'package:tutorials/view/login/forget_password_verify.dart'; +import 'package:tutorials/view/login/register.dart'; +import 'package:tutorials/view/login/forget_password.dart'; +import 'package:tutorials/view/login/register_finish.dart'; +import 'package:tutorials/view/login/register_verify.dart'; +import 'package:tutorials/view/name.card/name_card.dart'; +import 'package:tutorials/view/name.card/name_card_edit.dart'; +import 'package:tutorials/view/settings/env.dart'; +import 'package:tutorials/view/settings/env_edit.dart'; +import 'package:tutorials/view/settings/feedbacks.dart'; +import 'package:flutter/material.dart'; +import 'package:tutorials/constant/route_constant.dart'; +import 'package:tutorials/model/route_path.dart'; +import 'package:tutorials/view/about/about.dart'; +import 'package:tutorials/view/home/home.dart'; +import 'package:tutorials/view/profile/profile.dart'; +import 'package:tutorials/view/about/about_author.dart'; +import 'package:tutorials/view/settings/dev_tool.dart'; +import 'package:tutorials/view/settings/settings.dart'; + +class RouteConfiguration { + static List paths = [ + RoutePath( + RouteNameConstant.route_name_home, + (context, match) => Home(), + ), + RoutePath( + RouteNameConstant.route_name_home_statistics, + (context, match) => Statistics(), + ), + + + RoutePath( + RouteNameConstant.route_name_login, + (context, match) => Login(), + ), + RoutePath( + RouteNameConstant.route_name_register, + (context, match) => Register(), + ), + RoutePath( + RouteNameConstant.route_name_register_verify, + (context, match) => RegisterVerify(), + ), + RoutePath( + RouteNameConstant.route_name_register_finish, + (context, match) => RegisterFinish(), + ), + + RoutePath( + RouteNameConstant.route_name_forget_password, + (context, match) => ForgetPassword(), + ), + RoutePath( + RouteNameConstant.route_name_forget_password_verify, + (context, match) => ForgetPasswordVerify(), + ), + RoutePath( + RouteNameConstant.route_name_forget_password_finish, + (context, match) => ForgetPasswordFinish(), + ), + + RoutePath( + RouteNameConstant.route_name_profile, + (context, match) => Profile(), + ), + + RoutePath( + RouteNameConstant.route_name_settings, + (context, match) => Settings(), + ), + RoutePath( + RouteNameConstant.route_name_about, + (context, match) => About(), + ), + RoutePath( + RouteNameConstant.route_name_about_author, + (context, match) => AboutAuthor(), + ), + RoutePath( + RouteNameConstant.route_name_about_versions, + (context, match) => AboutVersions(), + ), + RoutePath( + RouteNameConstant.route_name_setting_dev_tool, + (context, match) => DevTool(), + ), + RoutePath( + RouteNameConstant.route_name_setting_feedbacks, + (context, match) => Feedbacks(), + ), + + RoutePath( + RouteNameConstant.route_name_name_card, + (context, match) => NameCard(), + ), + RoutePath( + RouteNameConstant.route_name_name_card_edit, + (context, match) => NameCardEdit(), + ), + RoutePath( + RouteNameConstant.route_name_env, + (context, match) => Environment(), + ), + RoutePath( + RouteNameConstant.route_name_env_edit, + (context, match) => EnvironmentEdit(), + ), + + + + ]; + + static Route? onGenerateRoute(RouteSettings settings) { + String? temp = settings?.name; + String name = temp ?? ""; + Logs.info('routeName = ${name}'); + // 有限匹配相等的 + for (final path in paths) { + if (name.compareTo(path.pattern) == 0) { + return MaterialPageRoute( + builder: (context) => path.builder(context, path.pattern), + settings: settings, + ); + } + } + // 在匹配正则的 + for (final path in paths) { + final regExpPattern = RegExp(path.pattern); + if (regExpPattern.hasMatch(name)) { + final firstMatch = regExpPattern.firstMatch(name); + final match = (firstMatch?.groupCount == 1) ? firstMatch?.group(1) : null; + return MaterialPageRoute( + builder: (context) => path.builder(context, match == null ? "" : match), + settings: settings, + ); + } + } + // If no match was found, we let [WidgetsApp.onUnknownRoute] handle it. + return null; + } +} diff --git a/study-app-tutorials/lib/startup/application.dart b/study-app-tutorials/lib/startup/application.dart new file mode 100644 index 000000000..2c4d8bd73 --- /dev/null +++ b/study-app-tutorials/lib/startup/application.dart @@ -0,0 +1,15 @@ +import 'package:tutorials/component/cache/http_request_caches.dart'; +import 'package:tutorials/component/cache/user_caches.dart'; +import 'package:tutorials/component/http/http_requests.dart'; +import 'package:tutorials/utils/app_utils.dart'; +import 'package:flutter/cupertino.dart'; + +class Application { + /// 应用初始化 + static void init(BuildContext context) { + HttpRequestCaches.inits(); + AppUtils.getDeviceId() + .then((deviceId) => {UserCaches.cacheUserId(deviceId.hashCode)}); + HttpRequests.init(); + } +} diff --git a/study-app-tutorials/lib/utils/app_utils.dart b/study-app-tutorials/lib/utils/app_utils.dart new file mode 100644 index 000000000..17714d1fb --- /dev/null +++ b/study-app-tutorials/lib/utils/app_utils.dart @@ -0,0 +1,72 @@ +import 'dart:io'; + +import 'package:flutter/widgets.dart'; +import 'package:tutorials/component/log/logs.dart'; +import 'package:connectivity/connectivity.dart'; +import 'package:flutter/services.dart'; +import 'package:permission_handler/permission_handler.dart'; +import 'package:device_info/device_info.dart'; + +class AppUtils { + static Future exitApp() async { + SystemChannels.platform.invokeMethod('SystemNavigator.pop'); + } + + static Future getDeviceId() async { + DeviceInfoPlugin deviceInfo = DeviceInfoPlugin(); + if (Platform.isAndroid) { + AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo; + print('android Running on ${androidInfo.model}'); // e.g. "Moto G (4)" + print('android id ${androidInfo.id}'); // + print('android id ${androidInfo.androidId}'); // + return androidInfo.id; + } + IosDeviceInfo iosInfo = await deviceInfo.iosInfo; + print('ios Running on ${iosInfo.utsname.machine}'); // e.g. "iPod7,1" + print('ios id ${iosInfo.identifierForVendor}'); // e.g. "iPod7,1" + print('ios name ${iosInfo.name}'); // e.g. "iPod7,1" + return iosInfo.identifierForVendor; + } + + static Future isConnected() async { + var connectivityResult = await (Connectivity().checkConnectivity()); + if (connectivityResult == ConnectivityResult.mobile) { + // I am connected to a mobile network. + Logs.info('I am connected to a mobile network.'); + return true; + } else if (connectivityResult == ConnectivityResult.wifi) { + // I am connected to a wifi network. + Logs.info('I am connected to a wifi network.'); + return true; + } + Logs.info('I am not connect to a network.'); + return false; + } + + static int getCurrentVersion() { + return 0; + } + + static Future openSettings() { + return openAppSettings(); + } + + static String getPackageId() { + return "com.bage.flutter.tutorials"; + } + + static void toPage( + BuildContext context, + String route, { + Object? args, + }) { + Navigator.of(context).pushNamed(route, arguments: args); + } + + static Object? getArgs(BuildContext context) { + return ModalRoute.of(context)?.settings?.arguments; + } + static void back(BuildContext context) { + Navigator.of(context).pop(); + } +} diff --git a/study-app-tutorials/lib/utils/crypt_utils.dart b/study-app-tutorials/lib/utils/crypt_utils.dart new file mode 100644 index 000000000..660557607 --- /dev/null +++ b/study-app-tutorials/lib/utils/crypt_utils.dart @@ -0,0 +1,14 @@ +import 'dart:convert'; + +import 'dart:typed_data'; + +class CryptUtils { + static String encode(String text) { + return base64Encode(utf8.encode(text)); + } + + static String decode(String text) { + Uint8List bytes = base64Decode(text); + return String.fromCharCodes(bytes); + } +} diff --git a/study-app-tutorials/lib/utils/date_time_utils.dart b/study-app-tutorials/lib/utils/date_time_utils.dart new file mode 100644 index 000000000..9191f6b99 --- /dev/null +++ b/study-app-tutorials/lib/utils/date_time_utils.dart @@ -0,0 +1,32 @@ +class DateTimeUtils { + /// + /// 2021-07-26 12:31:05 + /// + static DateTime parse(String? dateTimeStr) { + if (dateTimeStr == null) { + return DateTime.now(); + } + return DateTime.parse(dateTimeStr); + } + + /// + /// 2021-07-26T12:31:05 + /// + static DateTime parseFromJson(String? dateTimeStr) { + if (dateTimeStr == null) { + return DateTime.now(); + } + dateTimeStr = dateTimeStr.replaceAll('T', ' '); + return DateTime.parse(dateTimeStr); + } + + /// + /// 2021-07-26T12:31:05 + /// + static String format(DateTime dateTime) { + if (dateTime == null) { + dateTime = DateTime.now(); + } + return dateTime.toIso8601String(); + } +} diff --git a/study-app-tutorials/lib/utils/file_utils.dart b/study-app-tutorials/lib/utils/file_utils.dart new file mode 100644 index 000000000..d67a15682 --- /dev/null +++ b/study-app-tutorials/lib/utils/file_utils.dart @@ -0,0 +1,33 @@ +import 'dart:io'; + +import 'package:open_file/open_file.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:share/share.dart'; +import 'package:tutorials/component/cache/setting_caches.dart'; +import 'package:tutorials/component/log/logs.dart'; +import 'package:tutorials/component/sp/shared_preference_helper.dart'; +import 'package:tutorials/constant/sp_constant.dart'; + +class FileUtils { + static void openFile(File file) { + OpenFile.open(file.path) + .then((value) => print('openFile then value = ${value.message}')) + .catchError((error) => {print('openFile catchError error = $error')}); + } + + static Future getDownloadDirectory() async { + return SettingCaches.getDownloadDirectory(); + } + + static Future write(File file, var bytes) async { + try { + var raf = file.openSync(mode: FileMode.write); + raf.writeFromSync(bytes); + await raf.close(); + } catch (e) { + Logs.info(e.toString()); + return false; + } + return true; + } +} diff --git a/study-app-tutorials/lib/view/about/about.dart b/study-app-tutorials/lib/view/about/about.dart new file mode 100644 index 000000000..f19bb8e9f --- /dev/null +++ b/study-app-tutorials/lib/view/about/about.dart @@ -0,0 +1,119 @@ +import 'package:tutorials/component/dialog/dialogs.dart'; +import 'package:tutorials/component/http/http_requests.dart'; +import 'package:tutorials/constant/http_constant.dart'; +import 'package:tutorials/model/app_version.dart'; +import 'package:tutorials/utils/app_utils.dart'; +import 'package:flutter/material.dart'; +import 'package:tutorials/constant/route_constant.dart'; +import 'package:tutorials/locale/translations.dart'; +import 'package:share/share.dart'; + +class About extends StatefulWidget { + @override + _About createState() => new _About(); +} + +class _About extends State { + + late AppVersion _currentVersionInfo; + + @override + void initState() { + _currentVersionInfo = AppVersion.getCurrentVersionInfo(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(Translations.textOf(context, "about.title")), + ), + body: Container( + alignment: Alignment.center, + child: Column(children: [ + Container( + alignment: Alignment.center, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Image(image: AssetImage("assets/images/logo128.png")) + ], + ), + ), + Container( + alignment: Alignment.center, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text(Translations.textOf(context, "all.app.name"), + style: + TextStyle(fontSize: 17.0, fontWeight: FontWeight.bold)) + ], + ), + ), + Container( + alignment: Alignment.center, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text(_currentVersionInfo?.versionName??'') + ], + ), + ), + Container( + alignment: Alignment.center, + child: ListTile( + title: Text(Translations.textOf(context, "about.versions")), + trailing: Icon(Icons.chevron_right), + onTap: () { + Navigator.of(context) + .pushNamed(RouteNameConstant.route_name_about_versions); + }, + ), + ), + Container( + alignment: Alignment.center, + child: ListTile( + title: Text(Translations.textOf(context, "about.device.id")), + trailing: Icon(Icons.chevron_right), + onTap: () { + // 确认框 + showDevice(); + }, + ), + ), + Container( + alignment: Alignment.center, + child: ListTile( + title: Text(Translations.textOf(context, "about.author")), + trailing: Icon(Icons.chevron_right), + onTap: () { + Navigator.of(context) + .pushNamed(RouteNameConstant.route_name_about_author); + }, + ), + ), + Container( + alignment: Alignment.center, + child: ListTile( + title: Text(Translations.textOf(context, "about.share")), + trailing: Icon(Icons.chevron_right), + onTap: () { + String url = HttpRequests.rebuildUrl( + HttpConstant.url_settings_app_latest); + Share.share('【小陆,小陆,简简单单的小陆APP】 ${url} ', subject: ''); + }, + ), + ), + ]), + ), + ); + } + + void showDevice() { + AppUtils.getDeviceId().then((value) => { + Dialogs.showInfoDialog( + context, Translations.textOf(context, "about.device.id"), value) + }); + } +} diff --git a/study-app-tutorials/lib/view/about/about_author.dart b/study-app-tutorials/lib/view/about/about_author.dart new file mode 100644 index 000000000..b80146600 --- /dev/null +++ b/study-app-tutorials/lib/view/about/about_author.dart @@ -0,0 +1,121 @@ +import 'package:tutorials/locale/translations.dart'; +import 'package:tutorials/model/about_author_tab.dart'; +import 'package:tutorials/request/model/AuthorInfo.dart'; +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; + +import 'about_author_tab_view.dart'; + +class AboutAuthor extends StatefulWidget { + @override + _AboutAuthor createState() => new _AboutAuthor(); +} + +class _AboutAuthor extends State + with SingleTickerProviderStateMixin { + late TabController _tabController; //需要定义一个Controller + List tabs = []; + late AuthorInfo authorInfo; + bool isLoading = true; + + @override + void initState() { + tabs = AboutAuthorTabView.init(); + _tabController = TabController(length: tabs.length, vsync: this); + authorInfo = new AuthorInfo(); + authorInfo.iconUrl = "https://avatars.githubusercontent.com/u/18094768?v=4"; + authorInfo.homePageUrl = "https://github.com/bage2014"; + authorInfo.firstName = "陆"; + authorInfo.lastName = "瑞华"; + authorInfo.email = "893542907@qq.com"; + authorInfo.description = "上海某互联网,Java 研发工程师,5年研发服务端经验"; + isLoading = false; + } + + @override + Widget build(BuildContext context) { + const double edgeLeft = 16.0; + const double edgeRight = 16.0; + return Scaffold( + appBar: AppBar( + title: Text(Translations.textOf(context, "about.author.title")), + ), + body: Container( + alignment: Alignment.center, + child: Column(children: [ + Container( + padding: const EdgeInsets.fromLTRB(edgeLeft, 16.0, edgeRight, 0.0), + child: ClipOval( + child: CachedNetworkImage( + imageUrl: authorInfo.iconUrl ?? '', + placeholder: (context, url) => CircularProgressIndicator(), + errorWidget: (context, url, error) => + Image(image: AssetImage("assets/images/user_null.png")), + height: 128, + width: 128, + ), + ), + ), + Container( + padding: const EdgeInsets.fromLTRB(edgeLeft, 0.0, edgeRight, 0.0), + child: Text('${authorInfo.firstName ?? ""}${authorInfo.lastName ?? ""}', textScaleFactor: 1.4), + ), + Container( + padding: const EdgeInsets.fromLTRB(edgeLeft, 0.0, edgeRight, 0.0), + child: TabBar( + //生成Tab菜单 + controller: _tabController, + indicatorColor: Color(0xff66c97f), + //选中时下划线颜色,如果使用了indicator这里设置无效 + labelColor: Color(0xff66c97f), + unselectedLabelColor: Colors.black, + tabs: tabs + .map((e) => Tab( + text: e.text, + )) + .toList()), + ), + Expanded( + child: Container( + padding: const EdgeInsets.fromLTRB(edgeLeft, 0.0, edgeRight, 0.0), + child: TabBarView( + controller: _tabController, + children: tabs.map((e) { + //创建3个Tab页 + return isLoading ? LinearProgressIndicator( + backgroundColor: Colors.grey[200], + valueColor: AlwaysStoppedAnimation(Colors.blue), + ) : Container( + child: AboutAuthorTabView(tab: e,authorInfo: authorInfo), + ); + }).toList(), + ), + ), + ), + ]), + ), + ); + } + +// Map paramJson = new HashMap(); +// paramJson.putIfAbsent("targetPage", () => 1); +// paramJson.putIfAbsent("pageSize", () => 100); +// Map param = new HashMap(); +// param.putIfAbsent("param", () => json.encode(paramJson)); +// print(json.encode(paramJson)); +// list.add("hello"); +// HttpRequests.get(HttpConstant.url_tv_query_page, param, null) +// .then((result) { +// Logs.info('_onRefresh responseBody=' + result?.responseBody); +// setState(() { +// QueryTvResult tvResult = +// QueryTvResult.fromJson(json.decode(result?.responseBody)); +// if (tvResult.code == 200) { +// list = tvResult.data; +// } +// }); +// }).catchError((error) { +// print(error.toString()); +// }); + +} diff --git a/study-app-tutorials/lib/view/about/about_author_tab_view.dart b/study-app-tutorials/lib/view/about/about_author_tab_view.dart new file mode 100644 index 000000000..60e65978c --- /dev/null +++ b/study-app-tutorials/lib/view/about/about_author_tab_view.dart @@ -0,0 +1,244 @@ +import 'package:tutorials/locale/translations.dart'; +import 'package:tutorials/model/about_author_tab.dart'; +import 'package:tutorials/request/model/AuthorInfo.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +class AboutAuthorTabView extends StatelessWidget { + const AboutAuthorTabView({Key? key, required this.tab, required this.authorInfo}) : super(key: key); + + final TabTitle tab; + final AuthorInfo authorInfo; + static const String key_basic = 'basic'; + static const String key_activity = 'activity'; + + static List init() { + List tabs = []; + tabs.add(new TabTitle(key_basic, '基本信息')); + // tabs.add(new AboutAuthorTab(key_activity, '最新动态')); + return tabs; + } + + @override + Widget build(BuildContext context) { + switch (tab.key) { + case key_basic: + return _AboutAuthorTabViewBasic(authorInfo:authorInfo); + case key_activity: + return _AboutAuthorTabViewActivity(); + default: + return Text(''); + } + } +} + +class _AboutAuthorTabViewBasic extends StatelessWidget { + final AuthorInfo authorInfo; + + const _AboutAuthorTabViewBasic({Key? key,required this.authorInfo}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + margin: EdgeInsets.all(8.0), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ListTile( + leading: Icon(Icons.mail_outline), + title: Text('${authorInfo.email}'), + onTap: () {}, + ), + ListTile( + leading: Icon(Icons.link_outlined), + title: Text('${authorInfo.homePageUrl}'), + onTap: () {}, + ), + ListTile( + leading: Icon(Icons.info_outline), + title: Text('${authorInfo.description}'), + onTap: () {}, + ), + ], + ), + ); + } +} + +class _AboutAuthorTabViewActivity extends StatefulWidget { + @override + _AboutAuthorTabViewActivityState createState() => + new _AboutAuthorTabViewActivityState(); +} + +class _AboutAuthorTabViewActivityState + extends State<_AboutAuthorTabViewActivity> { + List<_OrderInfo> list = []; + + @override + void initState() { + list.add(_data(list.length + 1)); + } + + @override + Widget build(BuildContext context) { + return RefreshIndicator( + onRefresh: _onRefresh, + child: list.length == 0 + ? Center( + child: Text( + Translations.textOf(context, "all.list.view.no.data"), + textAlign: TextAlign.center, + ), + ) + : ListView.separated( + itemCount: list.length, + itemBuilder: (context, index) { + _OrderInfo data = list[index]; + return new GestureDetector( + onTap: () {}, + child: Card( + margin: EdgeInsets.all(8.0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: const EdgeInsets.all(20.0), + child: _OnTimeBar(driver: data), + ), + Divider(height: 1.0), + Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + data.driverInfo.name, + style: DefaultTextStyle.of(context).style.copyWith( + fontSize: 14.0, + ), + ), + ) + ], + ), + ), + ); + }, + separatorBuilder: (context, index) => Divider(height: .0), + ), + ); + } + + Future _onRefresh() async { + setState(() { + list.add(_data(list.length + 1)); + }); + } +} + +class _OnTimeBar extends StatelessWidget { + const _OnTimeBar({Key? key, required this.driver}) : super(key: key); + + final _OrderInfo driver; + + @override + Widget build(BuildContext context) { + return Row( + children: [ + MaterialButton( + onPressed: () { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(driver.driverInfo.name), + ), + ); + }, + elevation: 0, + shape: StadiumBorder(), + color: Color(0xff66c97f), + textColor: Colors.white, + child: Text('Activity'), + ), + Spacer(), + Text( + '${driver.date.day}/${driver.date.month}/${driver.date.year}', + style: TextStyle( + color: Color(0xffb6b2b2), + ), + ), + ], + ); + } +} + +_OrderInfo _data(int id) => _OrderInfo( + id: id, + date: DateTime.now(), + driverInfo: _DriverInfo( + name: 'Philipe', + thumbnailUrl: + 'https://i.pinimg.com/originals/08/45/81/084581e3155d339376bf1d0e17979dc6.jpg', + ), + deliveryProcesses: [ + _DeliveryProcess( + 'Event3', + _DeliveryMessage('13:00pm', 'This is a message for test'), + ), + _DeliveryProcess( + 'Event2', + _DeliveryMessage('13:00pm', 'This is a message for test'), + ), + _DeliveryProcess( + 'Event1', + _DeliveryMessage('11:30am', 'Reached halfway mark'), + ), + _DeliveryProcess.complete(), + ], + ); + +class _OrderInfo { + const _OrderInfo({ + required this.id, + required this.date, + required this.driverInfo, + required this.deliveryProcesses, + }); + + final int id; + final DateTime date; + final _DriverInfo driverInfo; + final List<_DeliveryProcess> deliveryProcesses; +} + +class _DriverInfo { + const _DriverInfo({ + required this.name, + required this.thumbnailUrl, + }); + + final String name; + final String thumbnailUrl; +} + +class _DeliveryProcess { + const _DeliveryProcess(this.name, this.message); + + const _DeliveryProcess.complete() + : this.name = 'Start', + this.message = const _DeliveryMessage('', ''); + + final String name; + final _DeliveryMessage message; + + bool get isCompleted => true; +} + +class _DeliveryMessage { + const _DeliveryMessage(this.createdAt, this.message); + + final String createdAt; // final DateTime createdAt; + final String message; + + @override + String toString() { + return '$createdAt $message'; + } +} diff --git a/study-app-tutorials/lib/view/about/about_user.dart b/study-app-tutorials/lib/view/about/about_user.dart new file mode 100644 index 000000000..eafc591db --- /dev/null +++ b/study-app-tutorials/lib/view/about/about_user.dart @@ -0,0 +1,285 @@ +import 'package:flutter/material.dart'; +import 'package:tutorials/component/log/logs.dart'; +import 'package:tutorials/utils/app_utils.dart'; + +class AboutUser extends StatefulWidget { + const AboutUser({Key? key}) : super(key: key); + + @override + _ProfileState createState() => _ProfileState(); +} + +class _ProfileState extends State { + List images = []; + + @override + Widget build(BuildContext context) { + return Scaffold( + body: SafeArea( + child: ListView( + children: [ + Padding( + padding: + EdgeInsets.only(left: 24, right: 24, top: 24, bottom: 24), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + GestureDetector( + onTap: () { + AppUtils.back(context); + }, + child: const Icon(Icons.arrow_back_outlined), + ), + ], + ), + ), + SizedBox(height: 8), + Padding( + padding: EdgeInsets.only(left: 24, right: 24, top: 16, bottom: 8), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + GestureDetector( + onTap: () { + Logs.info("GetIt.I.get().back();"); + }, + child: ClipOval( + child: Image( + image: AssetImage("assets/images/user_null.png")), + ), + ), + Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "刘亦菲", + style: TextStyle( + fontSize: 20, + color: Colors.black, + fontStyle: FontStyle.normal, + fontWeight: FontWeight.w600, + ), + ), + Text( + "中国,上海", + style: TextStyle( + fontSize: 14, + color: Colors.black, + fontStyle: FontStyle.normal, + fontWeight: FontWeight.w400, + ), + ), + ], + ), + Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + TextButton( + child: Padding( + padding: + EdgeInsets.symmetric(horizontal: 24, vertical: 6), + child: Text( + '已关注', + style: TextStyle( + fontSize: 14, + color: Colors.black, + fontStyle: FontStyle.normal, + fontWeight: FontWeight.w600, + ), + ), + ), + style: TextButton.styleFrom( + primary: Colors.white, + backgroundColor: const Color(0xffD0D0D0), + ), + onPressed: () {}, + ), + ], + ), + ], + ), + ), + SizedBox(height: 16), + Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + "9", + style: TextStyle( + fontSize: 20, + color: Colors.black, + fontStyle: FontStyle.normal, + fontWeight: FontWeight.w600, + ), + ), + Text( + "关注", + style: TextStyle( + fontSize: 14, + color: Colors.black, + fontStyle: FontStyle.normal, + fontWeight: FontWeight.w400, + ), + ), + ], + ), + ), + Expanded( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + "129K", + style: TextStyle( + fontSize: 20, + color: Colors.black, + fontStyle: FontStyle.normal, + fontWeight: FontWeight.w600, + ), + ), + Text( + "粉丝", + style: TextStyle( + fontSize: 14, + color: Colors.black, + fontStyle: FontStyle.normal, + fontWeight: FontWeight.w400, + ), + ), + ], + ), + ), + Expanded( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + "7.2M", + style: TextStyle( + fontSize: 20, + color: Colors.black, + fontStyle: FontStyle.normal, + fontWeight: FontWeight.w600, + ), + ), + Text( + "留言", + style: TextStyle( + fontSize: 14, + color: Colors.black, + fontStyle: FontStyle.normal, + fontWeight: FontWeight.w400, + ), + ), + ], + ), + ) + ], + ), + SizedBox(height: 32), + Container( + padding: EdgeInsets.only(left: 24, right: 24), + child: Text( + "关于", + style: TextStyle( + fontSize: 16, + color: Color(0xffA8A8A8), + fontWeight: FontWeight.w600, + ), + ), + ), + SizedBox( + height: 4, + ), + Container( + padding: EdgeInsets.only(left: 24, right: 25, bottom: 18), + child: Text( + "看了这组照片,网友们也明白了,为什么很多大牌商品喜欢请神仙姐姐去做代言了,因为她只要站在那里,隔着屏幕都能感受到满满的高级感。 别看这次出游,刘亦菲表现得十分欢脱,吃了很多美食...", + style: TextStyle( + fontSize: 14, + height: 1.57, + color: Color(0XFF000000), + fontWeight: FontWeight.w400, + ), + ), + ), + SizedBox(height: 16), + Container( + padding: EdgeInsets.only(left: 24, right: 25, bottom: 18), + child: Row( + children: [ + Expanded( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + const Icon(Icons.photo), + Padding( + padding: const EdgeInsets.only(left: 2), + child: Text( + "最新动态", + style: TextStyle( + fontSize: 14, + color: Colors.black, + fontStyle: FontStyle.normal, + fontWeight: FontWeight.w400, + ), + ), + ), + ], + ), + SizedBox( + height: 14, + ), + Container( + height: 2, + width: 76, + color: Colors.black, + ) + ], + ), + ), + ], + ), + ), + Padding( + padding: EdgeInsets.only(left: 13, right: 11), + child: GridView.count( + shrinkWrap: true, + crossAxisCount: 3, + crossAxisSpacing: 0.0, + mainAxisSpacing: 0.0, + children: List.generate( + images.length, + (index) { + return Container( + height: 122, + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage(images[index]), + fit: BoxFit.cover, + ), + ), + ); + }, + ), + ), + ) + ], + ), + ), + ); + } +} diff --git a/study-app-tutorials/lib/view/about/about_versions.dart b/study-app-tutorials/lib/view/about/about_versions.dart new file mode 100644 index 000000000..c239f570d --- /dev/null +++ b/study-app-tutorials/lib/view/about/about_versions.dart @@ -0,0 +1,195 @@ +import 'dart:collection'; +import 'dart:convert'; + +import 'package:tutorials/component/http/http_requests.dart'; +import 'package:tutorials/component/log/logs.dart'; +import 'package:tutorials/constant/http_constant.dart'; +import 'package:tutorials/locale/translations.dart'; +import 'package:tutorials/model/app_version.dart'; +import 'package:tutorials/request/model/AppVersionsResult.dart'; +import 'package:flutter/material.dart'; +import 'package:timelines/timelines.dart'; + +class AboutVersions extends StatefulWidget { + @override + _AboutVersions createState() => new _AboutVersions(); +} + +class _AboutVersions extends State { + List list = []; + bool isLoading = true; + + @override + void initState() { + _onRefresh(); + } + + @override + Widget build(BuildContext context) { + return Material( + child: CustomScrollView( + slivers: [ + //AppBar,包含一个导航栏 + SliverAppBar( + pinned: true, + expandedHeight: 250.0, + flexibleSpace: FlexibleSpaceBar( + title: Text(Translations.textOf(context, "about.versions")), + background: Image.asset("assets/images/logo128.png"), + ), + ), + //List + isLoading + ? SliverToBoxAdapter( + child: LinearProgressIndicator( + backgroundColor: Colors.grey[200], + valueColor: AlwaysStoppedAnimation(Colors.blue), + ), + ) + : new SliverFixedExtentList( + itemExtent: 1000.0, + delegate: new SliverChildBuilderDelegate( + (BuildContext context, int index) { + //创建列表项 + return _DeliveryProcesses(processes: list); + }, childCount: 1 //列表项 + ), + ), + ], + ), + ); + } + + void hideLoading() { + setState(() { + isLoading = false; + }); + } + + Future _onRefresh() async { + isLoading = true; + Map param = new HashMap(); + HttpRequests.get(HttpConstant.url_settings_app_versions, param, null) + .then((result) { + Logs.info('_onRefresh responseBody=' + (result?.responseBody ?? "")); + hideLoading(); + setState(() { + AppVersionsResult response = + AppVersionsResult.fromJson(json.decode(result?.responseBody ?? "")); + if (response.code == 200) { + list = response.data ?? []; + if (list.length > 0) { + AppVersion value = new AppVersion(); + value.versionName = Translations.textOf(context, 'about.version.start'); + list.insert(0, value); + } + } + }); + }).catchError((error) { + print(error.toString()); + hideLoading(); + }); + } +} + +class _DeliveryProcesses extends StatelessWidget { + const _DeliveryProcesses({Key? key, required this.processes}) + : super(key: key); + + final List processes; + + @override + Widget build(BuildContext context) { + return DefaultTextStyle( + style: TextStyle( + color: Color(0xff9b9b9b), + fontSize: 12.5, + ), + child: Padding( + padding: const EdgeInsets.all(20.0), + child: FixedTimeline.tileBuilder( + theme: TimelineThemeData( + nodePosition: 0, + color: Color(0xff989898), + indicatorTheme: IndicatorThemeData( + position: 0, + size: 20.0, + ), + connectorTheme: ConnectorThemeData( + thickness: 2.5, + ), + ), + builder: TimelineTileBuilder.connected( + connectionDirection: ConnectionDirection.before, + itemCount: processes.length, + contentsBuilder: (_, index) { + var version = processes[processes.length - 1 - index]; + if (version.versionCode == null) + return Padding( + padding: EdgeInsets.only(left: 8.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + version.versionName ?? '', + style: DefaultTextStyle.of(context).style.copyWith( + fontSize: 18.0, + ), + ), + ], + ), + ); + + return Padding( + padding: EdgeInsets.only(left: 8.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Row( + children: [ + Text( + '${Translations.textOf(context, 'about.version.name')} ${version.versionName}', + style: DefaultTextStyle.of(context).style.copyWith( + fontSize: 20.0, + ), + ), + Spacer(), + Text( + '${version.createTime?.year}-${version.createTime?.month}-${version.createTime?.day}', + style: TextStyle( + color: Color(0xffb6b2b2), + ), + ), + ], + ), + Padding( + padding: EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 0.0), + child: Text( + version.description?.replaceAll('|', '\n') ?? '', + ), + ), + ], + ), + ); + }, + indicatorBuilder: (_, index) { + return DotIndicator( + color: Color(0xff66c97f), + child: Icon( + Icons.check, + color: Colors.white, + size: 12.0, + ), + ); + }, + connectorBuilder: (_, index, ___) => SolidLineConnector( + color: Color(0xff66c97f), + ), + ), + ), + ), + ); + } +} diff --git a/study-app-tutorials/lib/view/family/Indicator.dart b/study-app-tutorials/lib/view/family/Indicator.dart new file mode 100644 index 000000000..e525a0c2a --- /dev/null +++ b/study-app-tutorials/lib/view/family/Indicator.dart @@ -0,0 +1,41 @@ +import 'package:flutter/material.dart'; + +class Indicator extends StatelessWidget { + final Color color; + final String text; + final bool isSquare; + final double size; + final Color textColor; + + const Indicator({ + Key? key, + required this.color, + required this.text, + required this.isSquare, + this.size = 16, + this.textColor = const Color(0xff505050), + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Row( + children: [ + Container( + width: size, + height: size, + decoration: BoxDecoration( + shape: isSquare ? BoxShape.rectangle : BoxShape.circle, + color: color, + ), + ), + const SizedBox( + width: 4, + ), + Text( + text, + style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: textColor), + ) + ], + ); + } +} \ No newline at end of file diff --git a/study-app-tutorials/lib/view/home/home.dart b/study-app-tutorials/lib/view/home/home.dart new file mode 100644 index 000000000..6b528677d --- /dev/null +++ b/study-app-tutorials/lib/view/home/home.dart @@ -0,0 +1,113 @@ +import 'package:tutorials/utils/app_utils.dart'; +import 'package:flutter/material.dart'; +import 'package:tutorials/component/toast/Toasts.dart'; +import 'package:tutorials/constant/route_constant.dart'; +import 'package:tutorials/locale/translations.dart'; +import 'package:tutorials/view/home/home_drawer.dart'; +import 'package:tutorials/view/home/menu_item.dart'; +import 'package:tutorials/view/home/statistics.dart'; + +class Home extends StatefulWidget { + @override + _ScaffoldRouteState createState() => _ScaffoldRouteState(); +} + +class _ScaffoldRouteState extends State { + List menuItems = []; //保存Icon数据 + DateTime _lastTime = DateTime.now(); //上次点击时间 + + // 初始化数据 + @override + void initState() { + // 初始化数据 + _retrieveIcons(); + super.initState(); + } + + @override + Widget build(BuildContext context) { + //获取路由参数 + var args = ModalRoute.of(context)?.settings?.arguments; + print('args=${args}'); + + return WillPopScope( + onWillPop: onWillPop, //重点此举 + child: Scaffold( + appBar: AppBar( + //导航栏 + title: Text(Translations.textOf(context, "all.app.name")), +// actions: [ +// //导航栏右侧菜单 +// IconButton(icon: Icon(Icons.share), onPressed: () {}), +// ], + ), + drawer: HomeDrawer(), //抽屉 +// floatingActionButton: FloatingActionButton( +// //悬浮按钮 +// child: Icon(Icons.add), +// onPressed: _onAdd), + body: Text('Test '), + // Statistics(), + // GridView.builder( + // gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + // crossAxisCount: 3, //每行三列 + // childAspectRatio: 1.4 //显示区域宽高相等 + // + // ), + // itemCount: menuItems.length, + // itemBuilder: (context, index) { + // return GestureDetector( + // onTap: () { + // MenuItem current = menuItems[index]; + // Navigator.of(context).pushNamed(current.route); + // }, + // child: Column( + // crossAxisAlignment: CrossAxisAlignment.center, + // children: [ + // Padding( + // padding: + // const EdgeInsets.fromLTRB(0, 16.0, 0, 0), + // child: Icon( + // menuItems[index].icon, + // size: 42, + // )), + // Padding( + // padding: + // const EdgeInsets.fromLTRB(0, 8.0, 0, 0), + // child: Text(menuItems[index].text)) + // ])); + // }, + // ), + ) + ); + } + + //模拟异步获取数据 + void _retrieveIcons() { + Future.delayed(Duration(milliseconds: 200)).then((e) { + setState(() { + menuItems.addAll([ + // new MenuItem(Icons.home_outlined, Translations.textOf(context, "home.menu.family"), + // RouteNameConstant.route_name_family_events), + MenuItem( + Icons.person, + Translations.textOf(context, "home.menu.profile"), + RouteNameConstant.route_name_home_statistics), + ]); + }); + }); + } + + Future onWillPop() async { + if (DateTime.now().difference(_lastTime) > Duration(seconds: 1)) { + //两次点击间隔超过1s重新计时 + _lastTime = DateTime.now(); + Toasts.show(Translations.textOf(context, "home.back.exit.double.tips")); + return false; + } + AppUtils.exitApp(); + return true; + } + + void _onAdd() {} +} diff --git a/study-app-tutorials/lib/view/home/home_drawer.dart b/study-app-tutorials/lib/view/home/home_drawer.dart new file mode 100644 index 000000000..76c5d0805 --- /dev/null +++ b/study-app-tutorials/lib/view/home/home_drawer.dart @@ -0,0 +1,166 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:tutorials/component/cache/user_caches.dart'; +import 'package:tutorials/component/dialog/dialogs.dart'; +import 'package:tutorials/component/event/event_bus.dart'; +import 'package:tutorials/component/log/logs.dart'; +import 'package:tutorials/constant/route_constant.dart'; +import 'package:tutorials/locale/translations.dart'; +import 'package:tutorials/request/model/user.dart'; +import 'package:tutorials/utils/app_utils.dart'; +import 'package:flutter/material.dart'; +import 'package:tutorials/view/home/locale_update_event.dart'; + +class HomeDrawer extends StatefulWidget { + @override + _HomeDrawer createState() { + return _HomeDrawer(); + } + +} + + +class _HomeDrawer extends State { + + late BuildContext _context; + + @override + void initState() { + super.initState(); + EventBus.consume((event) { + Logs.info('event = ${event.toString()}'); + setState(() { + + }); + }); + } + + @override + Widget build(BuildContext context) { + _context = context; + User user = UserCaches.getUser(); + Logs.info("user: ${user.toString()}"); + return Drawer( + child: MediaQuery.removePadding( + context: context, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(top: 68,bottom: 36), + child: GestureDetector( + onTap: () { + AppUtils.toPage( + context, RouteNameConstant.route_name_profile); + }, + child: Row( + children: [ + Padding( + padding: const EdgeInsets.only(left: 36), + child: ClipOval( + child: CachedNetworkImage( + imageUrl: user.iconUrl ?? '', + placeholder: (context, url) => + const CircularProgressIndicator(), + errorWidget: (context, url, error) => const Image( + image: AssetImage("assets/images/user_null.png")), + height: 86, + width: 86, + ), + ), + ), + Padding( + padding: const EdgeInsets.only(left: 16), + child: Text( + user.userName ?? + Translations.textOf(context, "all.app.name"), + style: const TextStyle( + fontSize: 20.0, fontWeight: FontWeight.bold))), + ], + ), + ), + ), + Expanded( + child: ListView( + padding: const EdgeInsets.only(left: 24), + children: [ + ListTile( + leading: const Icon(Icons.settings), + title: Text( + Translations.textOf(context, "home.drawer.settings")), + onTap: () { + Navigator.of(context) + .pushNamed(RouteNameConstant.route_name_settings); + }, + ), + ListTile( + leading: const Icon(Icons.info), + title: + Text(Translations.textOf(context, "home.drawer.about")), + onTap: () { + Navigator.of(context) + .pushNamed(RouteNameConstant.route_name_about); + }, + ), + ListTile( + leading: const Icon(Icons.exit_to_app), + title: + Text(Translations.textOf(context, "home.drawer.exit")), + onTap: () { + // 确认框 + checkExitApp(); + }, + ), + ListTile( + leading: const Icon(Icons.exit_to_app), + title: + Text(Translations.textOf(context, "home.drawer.logout")), + onTap: () { + // 确认框 + checkLogoutApp(); + }, + ), +// ListTile( +// leading: const Icon(Icons.person), +// title: Text(Translations.textOf(context, "home.drawer.profile")), +// onTap: () { +// Navigator.of(context) +// .pushNamed(RouteNameConstant.route_name_profile); +// }, +// ), +// ListTile( +// leading: const Icon(Icons.logout), +// title: const Text('Logout'), +// onTap: () { +// +// }, +// ), + ], + ), + ), + ], + ), + ), + ); + } + + Future checkExitApp() async { + String? showConfirmDialog = await Dialogs.showConfirmDialog( + _context, Translations.textOf(_context, "home.back.confirm"), null); + Logs.info('checkExitApp showConfirmDialog = $showConfirmDialog'); + if ("true" == showConfirmDialog) { + AppUtils.exitApp(); + } + } + + Future checkLogoutApp() async { + String? showConfirmDialog = await Dialogs.showConfirmDialog( + _context, Translations.textOf(_context, "home.logout.confirm"), null); + Logs.info('checkExitApp showConfirmDialog = $showConfirmDialog'); + if ("true" == showConfirmDialog) { + UserCaches.cacheUser(User()); // 清楚缓存、登陆状态 + Logs.info('cache user = ${UserCaches.getUser().toString()}'); + AppUtils.exitApp(); + } + } + +} diff --git a/study-app-tutorials/lib/view/home/locale_update_event.dart b/study-app-tutorials/lib/view/home/locale_update_event.dart new file mode 100644 index 000000000..0d14196c7 --- /dev/null +++ b/study-app-tutorials/lib/view/home/locale_update_event.dart @@ -0,0 +1,5 @@ +import 'package:tutorials/component/event/base_event.dart'; + +class LocaleUpdateEvent extends BaseEvent { + String data = ""; +} diff --git a/study-app-tutorials/lib/view/home/menu_item.dart b/study-app-tutorials/lib/view/home/menu_item.dart new file mode 100644 index 000000000..d8abacca3 --- /dev/null +++ b/study-app-tutorials/lib/view/home/menu_item.dart @@ -0,0 +1,9 @@ +import 'package:flutter/widgets.dart'; + +class MenuItem { + IconData icon; + String text; + String route; + + MenuItem(this.icon, this.text, this.route); +} diff --git a/study-app-tutorials/lib/view/home/statistics.dart b/study-app-tutorials/lib/view/home/statistics.dart new file mode 100644 index 000000000..eeabee53f --- /dev/null +++ b/study-app-tutorials/lib/view/home/statistics.dart @@ -0,0 +1,173 @@ + +import 'package:fl_chart/fl_chart.dart'; +import 'package:flutter/material.dart'; +import 'dart:math' as math; + +class Statistics extends StatefulWidget { + const Statistics({Key? key}) : super(key: key); + + @override + PieChartSample3State createState() => PieChartSample3State(); +} + +class PieChartSample3State extends State { + int touchedIndex = 0; + + @override + Widget build(BuildContext context) { + return AspectRatio( + aspectRatio: 1.3, + child: Card( + color: Colors.white, + child: AspectRatio( + aspectRatio: 1, + child: PieChart( + PieChartData( + pieTouchData: PieTouchData( + touchCallback: (FlTouchEvent event, pieTouchResponse) { + setState(() { + if (!event.isInterestedForInteractions || + pieTouchResponse == null || + pieTouchResponse.touchedSection == null) { + touchedIndex = -1; + return; + } + touchedIndex = + pieTouchResponse.touchedSection!.touchedSectionIndex; + }); + }), + borderData: FlBorderData( + show: false, + ), + sectionsSpace: 0, + centerSpaceRadius: 0, + sections: showingSections()), + ), + ), + ), + ); + } + + List showingSections() { + return List.generate(4, (i) { + final isTouched = i == touchedIndex; + final fontSize = isTouched ? 20.0 : 16.0; + final radius = isTouched ? 110.0 : 100.0; + final widgetSize = isTouched ? 55.0 : 40.0; + + switch (i) { + case 0: + return PieChartSectionData( + color: const Color(0xff0293ee), + value: 40, + title: '40%', + radius: radius, + titleStyle: TextStyle( + fontSize: fontSize, + fontWeight: FontWeight.bold, + color: const Color(0xffffffff)), + badgeWidget: _Badge( + 'assets/ophthalmology-svgrepo-com.svg', + size: widgetSize, + borderColor: const Color(0xff0293ee), + ), + badgePositionPercentageOffset: .98, + ); + case 1: + return PieChartSectionData( + color: const Color(0xfff8b250), + value: 30, + title: '30%', + radius: radius, + titleStyle: TextStyle( + fontSize: fontSize, + fontWeight: FontWeight.bold, + color: const Color(0xffffffff)), + badgeWidget: _Badge( + 'assets/librarian-svgrepo-com.svg', + size: widgetSize, + borderColor: const Color(0xfff8b250), + ), + badgePositionPercentageOffset: .98, + ); + case 2: + return PieChartSectionData( + color: const Color(0xff845bef), + value: 16, + title: '16%', + radius: radius, + titleStyle: TextStyle( + fontSize: fontSize, + fontWeight: FontWeight.bold, + color: const Color(0xffffffff)), + badgeWidget: _Badge( + 'assets/fitness-svgrepo-com.svg', + size: widgetSize, + borderColor: const Color(0xff845bef), + ), + badgePositionPercentageOffset: .98, + ); + case 3: + return PieChartSectionData( + color: const Color(0xff13d38e), + value: 15, + title: '15%', + radius: radius, + titleStyle: TextStyle( + fontSize: fontSize, + fontWeight: FontWeight.bold, + color: const Color(0xffffffff)), + badgeWidget: _Badge( + 'assets/worker-svgrepo-com.svg', + size: widgetSize, + borderColor: const Color(0xff13d38e), + ), + badgePositionPercentageOffset: .98, + ); + default: + throw 'Oh no'; + } + }); + } +} + +class _Badge extends StatelessWidget { + final String svgAsset; + final double size; + final Color borderColor; + + const _Badge( + this.svgAsset, { + Key? key, + required this.size, + required this.borderColor, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return AnimatedContainer( + duration: PieChart.defaultDuration, + width: size, + height: size, + decoration: BoxDecoration( + color: Colors.white, + shape: BoxShape.circle, + border: Border.all( + color: borderColor, + width: 2, + ), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(.5), + offset: const Offset(3, 3), + blurRadius: 3, + ), + ], + ), + padding: EdgeInsets.all(size * .15), + child: Center( + child: Image(image: AssetImage("assets/images/logo128.png")), + ), + ); + } +} \ No newline at end of file diff --git a/study-app-tutorials/lib/view/login/forget_password.dart b/study-app-tutorials/lib/view/login/forget_password.dart new file mode 100644 index 000000000..1ad74c7b3 --- /dev/null +++ b/study-app-tutorials/lib/view/login/forget_password.dart @@ -0,0 +1,166 @@ +import 'dart:convert'; + +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; +import 'package:tutorials/component/log/logs.dart'; +import 'package:tutorials/constant/color_constant.dart'; +import 'package:tutorials/constant/route_constant.dart'; +import 'package:tutorials/locale/translations.dart'; +import 'package:tutorials/request/model/forget/forget_password_request_param.dart'; +import 'package:tutorials/request/model/security_code_request_param.dart'; +import 'package:tutorials/request/security_code_requests.dart'; +import 'package:tutorials/utils/app_utils.dart'; + +class ForgetPassword extends StatefulWidget { + const ForgetPassword({Key? key}) : super(key: key); + + @override + _ForgetPasswordState createState() => _ForgetPasswordState(); +} + +class _ForgetPasswordState extends State { + TextEditingController userNameController = TextEditingController(); + TextEditingController securityCodeController = TextEditingController(); + + @override + Widget build(BuildContext context) { + String _imageUrl = SecurityCodeRequests.url(SecurityCodeRequestParam()); + return Scaffold( + appBar: AppBar( + backgroundColor: Colors.transparent, + elevation: 0, + iconTheme: IconThemeData(color: ColorConstant.app_bar_only_back_color), + ), + body: SafeArea( + child: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const Image(image: AssetImage("assets/images/logo128.png")), + const SizedBox(height: 24), + Text( + Translations.textOf(context, 'forget.password.title'), + style: const TextStyle( + fontSize: 20, + fontWeight: FontWeight.w600, + color: Color(0xFF262626), + ), + ), + const SizedBox(height: 24), + TextField( + controller: userNameController, + decoration: InputDecoration( + hintText: Translations.textOf( + context, 'forget.password.mail.hint'), + hintStyle: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.w400, + color: Color(0xFFA8A8A8), + ), + prefixIcon: + const Icon(Icons.email, color: Color(0xFFA8A8A8)), + contentPadding: const EdgeInsets.symmetric( + horizontal: 17, vertical: 22), + border: const OutlineInputBorder( + borderSide: BorderSide(color: Color(0xFFD0D0D0))), + focusedBorder: const OutlineInputBorder( + borderSide: BorderSide(color: Color(0xFFD0D0D0))), + enabledBorder: const OutlineInputBorder( + borderSide: BorderSide(color: Color(0xFFD0D0D0))), + ), + ), + const SizedBox(height: 14), + Row( + children: [ + Expanded( + child: TextField( + controller: securityCodeController, + decoration: InputDecoration( + hintText: Translations.textOf( + context, 'forget.password.security.code.hint'), + hintStyle: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.w400, + color: Color(0xFFA8A8A8), + ), + prefixIcon: const Icon(Icons.security_rounded, + color: Color(0xFFA8A8A8)), + contentPadding: const EdgeInsets.symmetric( + horizontal: 17, vertical: 22), + border: const OutlineInputBorder( + borderSide: BorderSide(color: Color(0xFFD0D0D0))), + focusedBorder: const OutlineInputBorder( + borderSide: BorderSide(color: Color(0xFFD0D0D0))), + enabledBorder: const OutlineInputBorder( + borderSide: BorderSide(color: Color(0xFFD0D0D0))), + ), + ), + ), + const SizedBox(width: 10), + GestureDetector( + onTap: () { + _imageUrl = SecurityCodeRequests.url( + SecurityCodeRequestParam()); + }, + child: Container( + width: 120, + height: 64, + decoration: BoxDecoration( + border: Border.all( + color: const Color(0xFFD0D0D0), + ), + ), + child: CachedNetworkImage( + imageUrl: _imageUrl, + placeholder: (context, url) => + const CircularProgressIndicator(), + errorWidget: (context, url, error) => const Image( + image: AssetImage("assets/images/user_null.png")), + height: 64, + width: 64, + ), + ), + ), + ], + ), + const SizedBox(height: 64), + ElevatedButton( + onPressed: () { + next(); + }, + style: ElevatedButton.styleFrom( + primary: const Color(0xFF0043CE), + elevation: 0, + shadowColor: Colors.transparent, + fixedSize: const Size(342, 64), + ), + child: Text( + Translations.textOf(context, 'forget.password.go'), + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: Color(0xFFF4F4F4), + ), + ), + ), + ], + ), + ), + ), + ), + ); + } + + void next() { + ForgetPasswordRequestParam param = ForgetPasswordRequestParam(); + param.userName = userNameController.text; + param.securityCode = securityCodeController.text; + String str = json.encode(param.toJson()); + Logs.info("json : $str"); + AppUtils.toPage( + context, RouteNameConstant.route_name_forget_password_verify, + args: str); + } +} diff --git a/study-app-tutorials/lib/view/login/forget_password_finish.dart b/study-app-tutorials/lib/view/login/forget_password_finish.dart new file mode 100644 index 000000000..10e8a8c62 --- /dev/null +++ b/study-app-tutorials/lib/view/login/forget_password_finish.dart @@ -0,0 +1,83 @@ +import 'package:flutter/material.dart'; +import 'package:tutorials/constant/color_constant.dart'; +import 'package:tutorials/constant/route_constant.dart'; +import 'package:tutorials/locale/translations.dart'; +import 'package:tutorials/utils/app_utils.dart'; + +class ForgetPasswordFinish extends StatefulWidget { + const ForgetPasswordFinish({Key? key}) : super(key: key); + + @override + _ForgetPasswordFinishState createState() => _ForgetPasswordFinishState(); +} + +class _ForgetPasswordFinishState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + backgroundColor: Colors.transparent, + elevation: 0, + iconTheme: IconThemeData(color: ColorConstant.app_bar_only_back_color), + ), + body: Column( + children: [ + const SizedBox(height: 64), + Container( + margin: const EdgeInsets.symmetric(horizontal: 40), + child: Column( + children: [ + Text( + Translations.textOf(context, 'forget.password.reset.finish.hint'), + style: const TextStyle( + fontSize: 24, + fontWeight: FontWeight.w600, + ), + ), + const SizedBox( + height: 24, + ), + Text( + Translations.textOf(context, 'forget.password.reset.to.login.hint'), + textAlign: TextAlign.center, + style: const TextStyle(), + ), + ], + ), + ), + const SizedBox(height: 64), + Container( + height: 128, + // color: Colors.green, + padding: const EdgeInsets.only(left: 50, right: 50), + child: const Image(image: AssetImage("assets/images/logo128.png")), + ), + const Spacer(), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: ElevatedButton( + onPressed: () { + AppUtils.toPage(context,RouteNameConstant.route_name_login); + }, + style: ElevatedButton.styleFrom( + primary: const Color(0xFF0043CE), + elevation: 0, + shadowColor: Colors.transparent, + fixedSize: const Size(342, 54), + ), + child: Text( + Translations.textOf(context, 'forget.password.reset.to.login'), + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: Colors.white, + ), + ), + ), + ), + const SizedBox(height: 88), + ], + ), + ); + } +} diff --git a/study-app-tutorials/lib/view/login/forget_password_verify.dart b/study-app-tutorials/lib/view/login/forget_password_verify.dart new file mode 100644 index 000000000..fe1dc1568 --- /dev/null +++ b/study-app-tutorials/lib/view/login/forget_password_verify.dart @@ -0,0 +1,203 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:tutorials/component/log/logs.dart'; +import 'package:tutorials/component/toast/Toasts.dart'; +import 'package:tutorials/constant/color_constant.dart'; +import 'package:tutorials/constant/error_code_constant.dart'; +import 'package:tutorials/constant/route_constant.dart'; +import 'package:tutorials/locale/translations.dart'; +import 'package:tutorials/request/forget_password_requests.dart'; +import 'package:tutorials/request/model/forget/forget_password_request_param.dart'; +import 'package:tutorials/utils/app_utils.dart'; +import 'package:tutorials/widgets/verification_text_field.dart'; + +class ForgetPasswordVerify extends StatefulWidget { + const ForgetPasswordVerify({Key? key}) : super(key: key); + + @override + _ForgetPasswordVerifyState createState() => _ForgetPasswordVerifyState(); +} + +class _ForgetPasswordVerifyState extends State { + bool _isLoading = false; + ForgetPasswordRequestParam param = ForgetPasswordRequestParam(); + + TextEditingController code1Controller = TextEditingController(); + TextEditingController code2Controller = TextEditingController(); + TextEditingController code3Controller = TextEditingController(); + TextEditingController code4Controller = TextEditingController(); + + @override + Widget build(BuildContext context) { + String str = AppUtils.getArgs(context).toString(); + Logs.info("json : $str"); + param = ForgetPasswordRequestParam.fromJson(json.decode(str)); + Logs.info("param : ${param.userName}"); + + Size size = MediaQuery.of(context).size; + + return Stack( + alignment: Alignment.center, //指定未定位或部分定位widget的对齐方式 + children: [ + Scaffold( + appBar: AppBar( + backgroundColor: Colors.transparent, + elevation: 0, + iconTheme: + IconThemeData(color: ColorConstant.app_bar_only_back_color), + ), + body: SafeArea( + child: SingleChildScrollView( + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 24), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + const SizedBox( + height: 64, + ), + Container( + margin: const EdgeInsets.symmetric(horizontal: 40), + child: Column( + children: [ + Text( + Translations.textOf( + context, 'forget.password.security.code.hint'), + style: const TextStyle( + fontSize: 20, + fontWeight: FontWeight.w600, + ), + ), + const SizedBox( + height: 24, + ), + Text( + Translations.textOf(context, + 'forget.password.security.code.send.hint'), + textAlign: TextAlign.center, + ), + Text( + param.userName ?? '', + textAlign: TextAlign.center, + style: const TextStyle( + decoration: TextDecoration.underline, + ), + ), + const SizedBox( + height: 100, + ), + Container( + height: 70, + width: size.width, + padding: const EdgeInsets.symmetric( + vertical: 0, horizontal: 0), + decoration: BoxDecoration( + // color: Colors.purple, + borderRadius: BorderRadius.circular(4), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + VerificationTextField(code1Controller), + VerificationTextField(code2Controller), + VerificationTextField(code3Controller), + VerificationTextField(code4Controller), + ], + ), + ), + ], + ), + ), + const SizedBox( + height: 100, + ), + SizedBox( + // color: Colors.blue, + height: 64, + child: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + ElevatedButton( + onPressed: () { + confirm(); + }, + style: ElevatedButton.styleFrom( + primary: const Color(0xFF0043CE), + elevation: 0, + shadowColor: Colors.transparent, + fixedSize: const Size(325, 50), + ), + child: Text( + Translations.textOf( + context, 'forget.password.reset.confirm'), + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: Colors.white, + ), + ), + ), + ], + ), + ), + ], + ), + ), + ), + ), + ), + Container( + child: _isLoading + ? Container( + color: Colors.black54.withOpacity(0.5), + width: double.infinity, + ) + : null, + ), + Container( + child: _isLoading + ? CircularProgressIndicator( + backgroundColor: Colors.grey[200], + valueColor: const AlwaysStoppedAnimation(Colors.blue), + ) + : null, + ), + ], + ); + } + + void confirm() { + showLoading(); + param.securityCode = code1Controller.text + + code2Controller.text + + code3Controller.text + + code4Controller.text; + + ForgetPasswordRequests.reset(param).then((result) { + Logs.info('login result=' + (result.toString() ?? "")); + hideLoading(); + if (result.common.code == ErrorCodeConstant.success) { + AppUtils.toPage( + context, RouteNameConstant.route_name_forget_password_finish); + } else { + Toasts.show(result.common.message); + } + }).catchError((error) { + Logs.info(error.toString()); + hideLoading(); + }); + } + + showLoading() { + setState(() { + _isLoading = true; + }); + } + + hideLoading() { + setState(() { + _isLoading = false; + }); + } +} diff --git a/study-app-tutorials/lib/view/login/login.dart b/study-app-tutorials/lib/view/login/login.dart new file mode 100644 index 000000000..c985729a7 --- /dev/null +++ b/study-app-tutorials/lib/view/login/login.dart @@ -0,0 +1,374 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; +import 'package:tutorials/component/cache/token_caches.dart'; +import 'package:tutorials/component/cache/user_caches.dart'; +import 'package:tutorials/component/log/logs.dart'; +import 'package:tutorials/component/toast/Toasts.dart'; +import 'package:tutorials/constant/error_code_constant.dart'; +import 'package:tutorials/constant/route_constant.dart'; +import 'package:tutorials/locale/translations.dart'; +import 'package:tutorials/request/login_requests.dart'; +import 'package:tutorials/request/model/user.dart'; +import 'package:tutorials/request/model/login/login_request_param.dart'; +import 'package:tutorials/request/model/security_code_request_param.dart'; +import 'package:tutorials/request/security_code_requests.dart'; +import 'package:tutorials/utils/app_utils.dart'; + +class Login extends StatefulWidget { + const Login({Key? key}) : super(key: key); + + @override + _LoginView createState() => _LoginView(); +} + +class _LoginView extends State { + bool _obscureText = true; + bool _isLoading = false; + bool _loginSecurityCodeRequired = false; + + TextEditingController userNameController = TextEditingController(); + TextEditingController passwordController = TextEditingController(); + TextEditingController securityCodeController = TextEditingController(); + + @override + void initState() { + super.initState(); + userNameController.text = ('bage2014@qq.com'); + passwordController.text = ('123456'); + + TokenCaches.getAccessToken().then((token) { + if (token?.isNotEmpty ?? false) { + Logs.info(' try login '); + // 直接尝试访问 + LoginRequests.tryLogin(token).then((result) { + if (result.common.code == ErrorCodeConstant.success) { + UserCaches.cacheUser(User.from(result)); + AppUtils.toPage(context, RouteNameConstant.route_name_home); + } + }); + } else { + Logs.info('need to login '); + } + }); + } + + @override + Widget build(BuildContext context) { + String _imageUrl = SecurityCodeRequests.url(SecurityCodeRequestParam()); + return Stack( + alignment: Alignment.center, //指定未定位或部分定位widget的对齐方式 + children: [ + Scaffold( + backgroundColor: const Color(0xFFFCFCFC), + body: SafeArea( + child: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const SizedBox(height: 40), + GestureDetector( + child: const Image( + image: AssetImage("assets/images/logo128.png")), + onDoubleTap: () { + envSetting(); + }, + ), + const SizedBox(height: 62), + Text( + Translations.textOf(context, 'login.title'), + style: const TextStyle( + fontSize: 20, + fontWeight: FontWeight.w600, + color: Color(0xFF262626), + ), + ), + const SizedBox(height: 24), + TextField( + controller: userNameController, + decoration: InputDecoration( + hintText: + Translations.textOf(context, 'login.account.hint'), + hintStyle: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.w400, + color: Color(0xFFA8A8A8), + ), + prefixIcon: + const Icon(Icons.person, color: Color(0xFFA8A8A8)), + contentPadding: const EdgeInsets.symmetric( + horizontal: 17, vertical: 22), + border: const OutlineInputBorder( + borderSide: BorderSide(color: Color(0xFFD0D0D0))), + focusedBorder: const OutlineInputBorder( + borderSide: BorderSide(color: Color(0xFFD0D0D0))), + enabledBorder: const OutlineInputBorder( + borderSide: BorderSide(color: Color(0xFFD0D0D0))), + ), + ), + const SizedBox(height: 14), + Row( + children: [ + Expanded( + child: TextField( + controller: passwordController, + obscureText: _obscureText, + decoration: InputDecoration( + hintText: Translations.textOf( + context, 'login.password.hint'), + hintStyle: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.w400, + color: Color(0xFFA8A8A8), + ), + suffixIcon: IconButton( + icon: Icon(!_obscureText + ? Icons.visibility + : Icons.visibility_off), + onPressed: () { + setState(() { + _obscureText = !_obscureText; + }); + }), + prefixIcon: const Icon(Icons.vpn_key, + color: Color(0xFFA8A8A8)), + contentPadding: const EdgeInsets.symmetric( + horizontal: 17, vertical: 22), + border: const OutlineInputBorder( + borderSide: + BorderSide(color: Color(0xFFD0D0D0))), + focusedBorder: const OutlineInputBorder( + borderSide: + BorderSide(color: Color(0xFFD0D0D0))), + enabledBorder: const OutlineInputBorder( + borderSide: + BorderSide(color: Color(0xFFD0D0D0))), + ), + ), + ), + ], + ), + const SizedBox(height: 14), + !_loginSecurityCodeRequired + ? const SizedBox(height: 0) + : Row( + children: [ + Expanded( + child: TextField( + controller: securityCodeController, + decoration: InputDecoration( + hintText: Translations.textOf( + context, 'login.security.hint'), + hintStyle: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.w400, + color: Color(0xFFA8A8A8), + ), + prefixIcon: const Icon( + Icons.security_rounded, + color: Color(0xFFA8A8A8)), + contentPadding: const EdgeInsets.symmetric( + horizontal: 17, vertical: 22), + border: const OutlineInputBorder( + borderSide: BorderSide( + color: Color(0xFFD0D0D0))), + focusedBorder: const OutlineInputBorder( + borderSide: BorderSide( + color: Color(0xFFD0D0D0))), + enabledBorder: const OutlineInputBorder( + borderSide: BorderSide( + color: Color(0xFFD0D0D0))), + ), + ), + ), + const SizedBox(width: 10), + GestureDetector( + onTap: () { + _imageUrl = SecurityCodeRequests.url( + SecurityCodeRequestParam()); + }, + child: Container( + width: 120, + height: 64, + decoration: BoxDecoration( + border: Border.all( + color: const Color(0xFFD0D0D0), + ), + ), + child: Center( + child: CachedNetworkImage( + imageUrl: _imageUrl, + placeholder: (context, url) => + const CircularProgressIndicator(), + errorWidget: (context, url, error) => + const Image( + image: AssetImage( + "assets/images/user_null.png")), + height: 64, + width: 120, + ), + ), + ), + ), + ], + ), + const SizedBox(height: 24), + ElevatedButton( + onPressed: () { + login(); + }, + style: ElevatedButton.styleFrom( + primary: const Color(0xFF0043CE), + elevation: 0, + shadowColor: Colors.transparent, + fixedSize: const Size(342, 64), + ), + child: Text( + Translations.textOf(context, 'login.button'), + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: Color(0xFFF4F4F4), + ), + ), + ), + const SizedBox(height: 16), + _button( + text: Translations.textOf(context, 'login.register'), + route: RouteNameConstant.route_name_register, + isTransparent: true), + _button( + text: Translations.textOf( + context, 'login.password.reset'), + route: RouteNameConstant.route_name_forget_password, + isTransparent: true), + ], + ), + ), + ), + ), + ), + Container( + child: _isLoading + ? Container( + color: Colors.black54.withOpacity(0.5), + width: double.infinity, + ) + : null, + ), + Container( + child: _isLoading + ? CircularProgressIndicator( + backgroundColor: Colors.grey[200], + valueColor: const AlwaysStoppedAnimation(Colors.blue), + ) + : null, + ), + ], + ); + } + + Widget _button( + {required String text, + required String route, + bool isTransparent = false}) => + ElevatedButton( + onPressed: () { + AppUtils.toPage(context, route); + }, + style: ElevatedButton.styleFrom( + primary: isTransparent ? Colors.transparent : const Color(0xFF0043CE), + elevation: 0, + shadowColor: Colors.transparent, + fixedSize: const Size(342, 64), + ), + child: Text( + text, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: isTransparent + ? const Color(0xFF0043CE) + : const Color(0xFFF4F4F4), + ), + ), + ); + + showLoading() { + setState(() { + _isLoading = true; + }); + } + + hideLoading() { + setState(() { + _isLoading = false; + }); + } + + void login() { + LoginRequestParam param = buildParam(); + + // 输入框校验 + bool ok = isOk(param); + if (!ok) { + return; + } + + showLoading(); + + LoginRequests.login(param).then((result) { + Logs.info('login result=' + (result.toString() ?? "")); + hideLoading(); + if (result.common.code == ErrorCodeConstant.success) { + _loginSecurityCodeRequired = false; + Toasts.show(Translations.textOf(context, "login.success.toast")); + UserCaches.cacheUser(User.from(result)); + AppUtils.toPage(context, RouteNameConstant.route_name_home); + } else if (result.common.code == + ErrorCodeConstant.loginSecurityCodeRequired) { + Toasts.show(result.common.message); + _loginSecurityCodeRequired = true; + } else { + Toasts.show(result.common.message); + } + }).catchError((error) { + Toasts.show(error.toString()); + Logs.info(error.toString()); + hideLoading(); + }); + } + + LoginRequestParam buildParam() { + LoginRequestParam param = LoginRequestParam(); + param.userName = userNameController.text; + param.password = passwordController.text; + param.securityCode = securityCodeController.text; + return param; + } + + void envSetting() async { + Navigator.of(context).pushNamed(RouteNameConstant.route_name_env); + } + + bool isOk(LoginRequestParam param) { + if (param.userName == null || (param.userName?.isEmpty ?? true)) { + Toasts.show(Translations.textOf(context, 'login.validation.username')); + return false; + } + + if (param.password == null || (param.password?.isEmpty ?? true)) { + Toasts.show(Translations.textOf(context, 'login.validation.password')); + return false; + } + + if (_loginSecurityCodeRequired && + (param.securityCode == null || (param.securityCode?.isEmpty ?? true))) { + Toasts.show(Translations.textOf(context, 'login.validation.security')); + return false; + } + + return true; + } +} diff --git a/study-app-tutorials/lib/view/login/register.dart b/study-app-tutorials/lib/view/login/register.dart new file mode 100644 index 000000000..6c45cf5d7 --- /dev/null +++ b/study-app-tutorials/lib/view/login/register.dart @@ -0,0 +1,289 @@ +import 'dart:convert'; + +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; +import 'package:tutorials/component/log/logs.dart'; +import 'package:tutorials/component/toast/Toasts.dart'; +import 'package:tutorials/constant/color_constant.dart'; +import 'package:tutorials/constant/route_constant.dart'; +import 'package:tutorials/locale/translations.dart'; +import 'package:tutorials/request/model/register/register_request_param.dart'; +import 'package:tutorials/request/model/security_code_request_param.dart'; +import 'package:tutorials/request/security_code_requests.dart'; +import 'package:tutorials/utils/app_utils.dart'; + +class Register extends StatefulWidget { + const Register({Key? key}) : super(key: key); + + @override + _RegisterState createState() => _RegisterState(); +} + +class _RegisterState extends State { + bool _obscureText = true; + bool _obscureText2 = true; + TextEditingController userNameController = TextEditingController(); + TextEditingController passwordController = TextEditingController(); + TextEditingController passwordAgainController = TextEditingController(); + TextEditingController securityCodeController = TextEditingController(); + + + @override + void initState() { + super.initState(); + userNameController.text = ('bage1001@qq.com'); + passwordController.text = ('123456'); + passwordAgainController.text = ('123456'); + securityCodeController.text = ('5374'); + } + + @override + Widget build(BuildContext context) { + String _imageUrl = SecurityCodeRequests.url(SecurityCodeRequestParam()); + return Scaffold( + appBar: AppBar( + backgroundColor: Colors.transparent, + elevation: 0, + iconTheme: IconThemeData(color: ColorConstant.app_bar_only_back_color), + ), + body: SafeArea( + child: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const Image(image: AssetImage("assets/images/logo128.png")), + const SizedBox(height: 16), + Text( + Translations.textOf(context, 'register.hint'), + style: const TextStyle( + fontSize: 20, + fontWeight: FontWeight.w600, + color: Color(0xFF262626), + ), + ), + const SizedBox(height: 24), + TextField( + controller: userNameController, + decoration: InputDecoration( + hintText: + Translations.textOf(context, 'register.mail.hint'), + hintStyle: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.w400, + color: Color(0xFFA8A8A8), + ), + suffixIcon: + const Icon(Icons.email, color: Color(0xFFA8A8A8)), + prefixIcon: + const Icon(Icons.vpn_key, color: Color(0xFFA8A8A8)), + contentPadding: const EdgeInsets.symmetric( + horizontal: 17, vertical: 22), + border: const OutlineInputBorder( + borderSide: BorderSide(color: Color(0xFFD0D0D0))), + focusedBorder: const OutlineInputBorder( + borderSide: BorderSide(color: Color(0xFFD0D0D0))), + enabledBorder: const OutlineInputBorder( + borderSide: BorderSide(color: Color(0xFFD0D0D0))), + ), + ), + const SizedBox(height: 14), + Row( + children: [ + Expanded( + child: TextField( + controller: passwordController, + obscureText: _obscureText, + decoration: InputDecoration( + hintText: Translations.textOf( + context, 'register.password.hint'), + hintStyle: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.w400, + color: Color(0xFFA8A8A8), + ), + suffixIcon: IconButton( + icon: Icon(!_obscureText + ? Icons.visibility + : Icons.visibility_off), + onPressed: () { + setState(() { + _obscureText = !_obscureText; + }); + }), + prefixIcon: const Icon(Icons.vpn_key, + color: Color(0xFFA8A8A8)), + contentPadding: const EdgeInsets.symmetric( + horizontal: 17, vertical: 22), + border: const OutlineInputBorder( + borderSide: BorderSide(color: Color(0xFFD0D0D0))), + focusedBorder: const OutlineInputBorder( + borderSide: BorderSide(color: Color(0xFFD0D0D0))), + enabledBorder: const OutlineInputBorder( + borderSide: BorderSide(color: Color(0xFFD0D0D0))), + ), + ), + ), + ], + ), + const SizedBox(height: 14), + Row( + children: [ + Expanded( + child: TextField( + obscureText: _obscureText2, + controller: passwordAgainController, + decoration: InputDecoration( + hintText: Translations.textOf( + context, 'register.password.again.hint'), + hintStyle: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.w400, + color: Color(0xFFA8A8A8), + ), + suffixIcon: IconButton( + icon: Icon(!_obscureText2 + ? Icons.visibility + : Icons.visibility_off), + onPressed: () { + setState(() { + _obscureText2 = !_obscureText2; + }); + }), + prefixIcon: const Icon(Icons.vpn_key, + color: Color(0xFFA8A8A8)), + contentPadding: const EdgeInsets.symmetric( + horizontal: 17, vertical: 22), + border: const OutlineInputBorder( + borderSide: BorderSide(color: Color(0xFFD0D0D0))), + focusedBorder: const OutlineInputBorder( + borderSide: BorderSide(color: Color(0xFFD0D0D0))), + enabledBorder: const OutlineInputBorder( + borderSide: BorderSide(color: Color(0xFFD0D0D0))), + ), + ), + ), + ], + ), + const SizedBox(height: 14), + Row( + children: [ + Expanded( + child: TextField( + controller: securityCodeController, + obscureText: false, + decoration: InputDecoration( + hintText: Translations.textOf( + context, 'register.security.hint'), + hintStyle: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.w400, + color: Color(0xFFA8A8A8), + ), + prefixIcon: const Icon(Icons.security_rounded, + color: Color(0xFFA8A8A8)), + contentPadding: const EdgeInsets.symmetric( + horizontal: 17, vertical: 22), + border: const OutlineInputBorder( + borderSide: BorderSide(color: Color(0xFFD0D0D0))), + focusedBorder: const OutlineInputBorder( + borderSide: BorderSide(color: Color(0xFFD0D0D0))), + enabledBorder: const OutlineInputBorder( + borderSide: BorderSide(color: Color(0xFFD0D0D0))), + ), + ), + ), + const SizedBox(width: 10), + GestureDetector( + onTap: () { + _imageUrl = SecurityCodeRequests.url( + SecurityCodeRequestParam()); + }, + child: Container( + width: 120, + height: 64, + decoration: BoxDecoration( + border: Border.all( + color: const Color(0xFFD0D0D0), + ), + ), + child: CachedNetworkImage( + imageUrl: _imageUrl, + placeholder: (context, url) => + const CircularProgressIndicator(), + errorWidget: (context, url, error) => const Image( + image: AssetImage("assets/images/user_null.png")), + height: 64, + width: 64, + ), + ), + ), + ], + ), + const SizedBox(height: 24), + ElevatedButton( + onPressed: () { + next(); + }, + style: ElevatedButton.styleFrom( + primary: const Color(0xFF0043CE), + elevation: 0, + shadowColor: Colors.transparent, + fixedSize: const Size(342, 64), + ), + child: Text( + Translations.textOf(context, 'register.next'), + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: Color(0xFFF4F4F4), + ), + ), + ), + ], + ), + ), + ), + ), + ); + } + + void next() { + RegisterRequestParam param = RegisterRequestParam(); + param.userName = userNameController.text; + param.password = passwordController.text; + param.securityCode = securityCodeController.text; + + bool fail = checkParam(param); + if(fail){ + return ; + } + String str = json.encode(param.toJson()); + Logs.info("json : $str"); + AppUtils.toPage(context, RouteNameConstant.route_name_register_verify, + args: str); + } + + + bool checkParam(RegisterRequestParam param) { + if((param.userName?.length??0) <= 0){ + Toasts.show('user name can not be empty'); + return true; + } + if((param.password?.length??0) <= 0){ + Toasts.show('password can not be empty'); + return true; + } + if((param.password?.length??0) != (passwordAgainController.text?.length??0)){ + Toasts.show('password not same'); + return true; + } + if((param.securityCode?.length??0) <= 0){ + Toasts.show('security code can not be empty'); + return true; + } + return false; + + } +} diff --git a/study-app-tutorials/lib/view/login/register_finish.dart b/study-app-tutorials/lib/view/login/register_finish.dart new file mode 100644 index 000000000..f863b044a --- /dev/null +++ b/study-app-tutorials/lib/view/login/register_finish.dart @@ -0,0 +1,84 @@ +import 'package:flutter/material.dart'; +import 'package:tutorials/constant/color_constant.dart'; +import 'package:tutorials/constant/route_constant.dart'; +import 'package:tutorials/locale/translations.dart'; +import 'package:tutorials/utils/app_utils.dart'; + +class RegisterFinish extends StatefulWidget { + const RegisterFinish({Key? key}) : super(key: key); + + @override + _RegisterFinishState createState() => _RegisterFinishState(); +} + +class _RegisterFinishState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + backgroundColor: Colors.transparent, + elevation: 0, + iconTheme: IconThemeData(color: ColorConstant.app_bar_only_back_color), + ), + body: Column( + children: [ + const SizedBox(height: 64), + Container( + margin: const EdgeInsets.symmetric(horizontal: 40), + child: Column( + children: [ + Text( + Translations.textOf(context, 'register.finish.title'), + style: const TextStyle( + fontSize: 24, + fontWeight: FontWeight.w600, + ), + ), + const SizedBox( + height: 24, + ), + Text( + Translations.textOf(context, 'register.finish.hint'), + textAlign: TextAlign.center, + style: const TextStyle( + ), + ), + ], + ), + ), + const SizedBox(height: 64), + Container( + height: 128, + // color: Colors.green, + padding: const EdgeInsets.only(left: 50, right: 50), + child: const Image(image: AssetImage("assets/images/logo128.png")), + ), + const Spacer(), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: ElevatedButton( + onPressed: () { + AppUtils.toPage(context,RouteNameConstant.route_name_login); + }, + style: ElevatedButton.styleFrom( + primary: const Color(0xFF161616), + elevation: 0, + shadowColor: Colors.transparent, + fixedSize: const Size(342, 54), + ), + child: Text( + Translations.textOf(context, 'register.finish.login'), + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: Colors.white, + ), + ), + ), + ), + const SizedBox(height: 88), + ], + ), + ); + } +} diff --git a/study-app-tutorials/lib/view/login/register_verify.dart b/study-app-tutorials/lib/view/login/register_verify.dart new file mode 100644 index 000000000..175dcdd92 --- /dev/null +++ b/study-app-tutorials/lib/view/login/register_verify.dart @@ -0,0 +1,219 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:tutorials/component/log/logs.dart'; +import 'package:tutorials/component/toast/Toasts.dart'; +import 'package:tutorials/constant/color_constant.dart'; +import 'package:tutorials/constant/error_code_constant.dart'; +import 'package:tutorials/constant/route_constant.dart'; +import 'package:tutorials/locale/translations.dart'; +import 'package:tutorials/request/model/register/register_request_param.dart'; +import 'package:tutorials/request/register_requests.dart'; +import 'package:tutorials/utils/app_utils.dart'; +import 'package:tutorials/widgets/verification_text_field.dart'; + +class RegisterVerify extends StatefulWidget { + const RegisterVerify({Key? key}) : super(key: key); + + @override + _RegisterVerifyState createState() => _RegisterVerifyState(); +} + +class _RegisterVerifyState extends State { + bool _isLoading = false; + RegisterRequestParam param = RegisterRequestParam(); + + TextEditingController code1Controller = TextEditingController(); + TextEditingController code2Controller = TextEditingController(); + TextEditingController code3Controller = TextEditingController(); + TextEditingController code4Controller = TextEditingController(); + + @override + Widget build(BuildContext context) { + Size size = MediaQuery.of(context).size; + + String str = AppUtils.getArgs(context).toString(); + Logs.info("json : $str"); + param = RegisterRequestParam.fromJson(json.decode(str)); + Logs.info("param : ${param.userName}"); + return Stack( + alignment: Alignment.center, //指定未定位或部分定位widget的对齐方式 + children: [ + Scaffold( + appBar: AppBar( + backgroundColor: Colors.transparent, + elevation: 0, + iconTheme: + IconThemeData(color: ColorConstant.app_bar_only_back_color), + ), + body: SafeArea( + child: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 24), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + const SizedBox( + height: 64, + ), + Container( + margin: const EdgeInsets.symmetric(horizontal: 40), + child: Column( + children: [ + Text( + Translations.textOf( + context, 'register.security.hint'), + style: const TextStyle( + fontSize: 20, + fontWeight: FontWeight.w600, + ), + ), + const SizedBox( + height: 24, + ), + Text( + Translations.textOf( + context, 'register.security.code.send.hint'), + textAlign: TextAlign.center, + ), + Text( + param.userName ?? '', + textAlign: TextAlign.center, + style: const TextStyle( + decoration: TextDecoration.underline, + ), + ), + const SizedBox( + height: 84, + ), + Container( + height: 64, + width: size.width, + padding: const EdgeInsets.symmetric( + vertical: 0, horizontal: 0), + decoration: BoxDecoration( + // color: Colors.purple, + borderRadius: BorderRadius.circular(4), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + VerificationTextField(code1Controller), + VerificationTextField(code2Controller), + VerificationTextField(code3Controller), + VerificationTextField(code4Controller), + ], + ), + ), + ], + ), + ), + const SizedBox( + height: 96, + ), + SizedBox( + // color: Colors.blue, + height: 64, + child: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + ElevatedButton( + onPressed: () { + confirm(); + }, + style: ElevatedButton.styleFrom( + primary: const Color(0xFF161616), + elevation: 0, + shadowColor: Colors.transparent, + fixedSize: const Size(325, 50), + ), + child: Text( + Translations.textOf(context, 'register.go'), + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: Colors.white, + ), + ), + ), + ], + ), + ), + ], + ), + ), + ), + ), + ), + Container( + child: _isLoading + ? Container( + color: Colors.black54.withOpacity(0.5), + width: double.infinity, + ) + : null, + ), + Container( + child: _isLoading + ? CircularProgressIndicator( + backgroundColor: Colors.grey[200], + valueColor: const AlwaysStoppedAnimation(Colors.blue), + ) + : null, + ), + ], + ); + } + + void confirm() { + param.securityCode = code1Controller.text + + code2Controller.text + + code3Controller.text + + code4Controller.text; + + bool fail = checkParam(param); + if(fail){ + return ; + } + + + showLoading(); + RegisterRequests.register(param).then((result) { + Logs.info('login result=' + (result.toString() ?? "")); + hideLoading(); + if (result.common.code == ErrorCodeConstant.success) { + AppUtils.toPage(context, RouteNameConstant.route_name_register_finish); + } else { + Toasts.show(result.common.message); + } + }).catchError((error) { + Logs.info(error.toString()); + hideLoading(); + }); + } + + showLoading() { + setState(() { + _isLoading = true; + }); + } + + hideLoading() { + setState(() { + _isLoading = false; + }); + } + + bool checkParam(RegisterRequestParam param) { + if((param.securityCode?.length??0) <= 0){ + Toasts.show('security code can not be empty'); + return true; + } + if((param.securityCode?.length??0) < 4){ + Toasts.show('security code is un valid'); + return true; + } + return false; + + } +} diff --git a/study-app-tutorials/lib/view/name.card/name_card.dart b/study-app-tutorials/lib/view/name.card/name_card.dart new file mode 100644 index 000000000..d76a661a5 --- /dev/null +++ b/study-app-tutorials/lib/view/name.card/name_card.dart @@ -0,0 +1,120 @@ +import 'dart:io'; + +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:dio/dio.dart'; +import 'package:flutter/material.dart'; +import 'package:tutorials/component/image.crop/image_cropper.dart'; +import 'package:tutorials/component/log/logs.dart'; +import 'package:tutorials/component/picker/image_picker.dart'; +import 'package:tutorials/constant/route_constant.dart'; +import 'package:tutorials/locale/translations.dart'; +import 'package:tutorials/request/file_upload_request.dart'; +import 'package:tutorials/request/model/upload/file_upload_param.dart'; +import 'package:tutorials/utils/app_utils.dart'; +import 'package:tutorials/view/name.card/name_card_basic.dart'; +import 'package:tutorials/view/name.card/name_card_school.dart'; +import 'package:tutorials/widgets/profile_icon_basic.dart'; + +class NameCard extends StatefulWidget { + const NameCard({Key? key}) : super(key: key); + + @override + _NameCardState createState() => _NameCardState(); +} + +class _NameCardState extends State { + List list = []; + bool _isLoading = false; + String url = ""; + + @override + void initState() { + super.initState(); + _onRefresh(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(Translations.textOf(context, "name.card.title")), + ), + body: SafeArea( + child: RefreshIndicator( + onRefresh: _onRefresh, + child: Container( + child: _isLoading + ? Center( + child: CircularProgressIndicator( + backgroundColor: Colors.grey[200], + valueColor: const AlwaysStoppedAnimation(Colors.blue), + )) + : (list.isEmpty + ? Center( + child: Text( + Translations.textOf(context, "all.list.view.no.data"), + textAlign: TextAlign.center, + ), + ) + : ListView.separated( + itemCount: list.length, + shrinkWrap: true, + itemBuilder: (context, index) { + return GestureDetector( + onTap: () {}, + child: NameCardBasic( + title: '学校信息', + subTitle: '河海大学', + desc: '2013年-2017年 计算机科学与技术专业!', + url: + 'https://avatars.githubusercontent.com/u/18094768?v=4', + onTap: () { + AppUtils.toPage(context, + RouteNameConstant.route_name_name_card_edit, + args: list[index]); + }, + )); + }, + separatorBuilder: (context, index) => Divider(height: .0), + )), + ), + )), + ); + } + + Future _onRefresh() async { + setState(() { + list.add('value1'); + list.add('value2'); + list.add('value3'); + list.add('value4'); + list.add('value5'); + list.add('value6'); + list.add('value7'); + }); + } + + pickBack(String? value) async { + Logs.info('image picked: $value'); + if (value != null) { + String valueCrop = await _cropImage(value); + setState(() { + url = valueCrop; + }); + + FileUploadParam param = FileUploadParam(); + param.files = [MultipartFile.fromFileSync(url)]; + FileUploadRequests.upload(param, (count, total) {}).then((value) => {}); + } else { + setState(() { + url = url == "assets/images/logo128.png" + ? "assets/images/user_null.png" + : "assets/images/logo128.png"; + }); + } + } + + Future _cropImage(String valueCrop) async { + return await ImageCropper.cropImage(context, valueCrop); + } +} diff --git a/study-app-tutorials/lib/view/name.card/name_card2.dart b/study-app-tutorials/lib/view/name.card/name_card2.dart new file mode 100644 index 000000000..085244239 --- /dev/null +++ b/study-app-tutorials/lib/view/name.card/name_card2.dart @@ -0,0 +1,266 @@ +import 'dart:io'; + +import 'package:dio/dio.dart'; +import 'package:flutter/material.dart'; +import 'package:tutorials/component/image.crop/image_cropper.dart'; +import 'package:tutorials/component/log/logs.dart'; +import 'package:tutorials/component/picker/image_picker.dart'; +import 'package:tutorials/locale/translations.dart'; +import 'package:tutorials/request/file_upload_request.dart'; +import 'package:tutorials/request/model/upload/file_upload_param.dart'; +import 'package:tutorials/widgets/profile_icon_basic.dart'; + +class NameCard2 extends StatefulWidget { + const NameCard2({Key? key}) : super(key: key); + + @override + _NameCardState createState() => _NameCardState(); +} + +class _NameCardState extends State { + List images = []; + + String url = "assets/images/user_null.png"; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(Translations.textOf(context, "name.card.title")), + ), + body: SafeArea( + child: ListView( + children: [ + const SizedBox(height: 8), + url.startsWith('assets') + ? ProfileIconBasic( + url: url, + onTap: () { + ImagePicker.pickImage() + .then((value) => {pickBack(value)}); + }, + ) + : GestureDetector( + onTap: () { + ImagePicker.pickImage() + .then((value) => {pickBack(value)}); + }, + child: Image.file( + File(url), + width: 150, + height: 150, + ), + ), + const SizedBox(height: 16), + Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + "9", + style: TextStyle( + fontSize: 20, + color: Colors.black, + fontStyle: FontStyle.normal, + fontWeight: FontWeight.w600, + ), + ), + Text( + "关注", + style: TextStyle( + fontSize: 14, + color: Colors.black, + fontStyle: FontStyle.normal, + fontWeight: FontWeight.w400, + ), + ), + ], + ), + ), + Expanded( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + "12K", + style: TextStyle( + fontSize: 20, + color: Colors.black, + fontStyle: FontStyle.normal, + fontWeight: FontWeight.w600, + ), + ), + Text( + "粉丝", + style: TextStyle( + fontSize: 14, + color: Colors.black, + fontStyle: FontStyle.normal, + fontWeight: FontWeight.w400, + ), + ), + ], + ), + ), + Expanded( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + "7.2M", + style: TextStyle( + fontSize: 20, + color: Colors.black, + fontStyle: FontStyle.normal, + fontWeight: FontWeight.w600, + ), + ), + Text( + "留言", + style: TextStyle( + fontSize: 14, + color: Colors.black, + fontStyle: FontStyle.normal, + fontWeight: FontWeight.w400, + ), + ), + ], + ), + ) + ], + ), + SizedBox(height: 32), + Container( + padding: EdgeInsets.only(left: 24, right: 24), + child: Text( + "简介", + style: TextStyle( + fontSize: 16, + color: Color(0xffA8A8A8), + fontWeight: FontWeight.w600, + ), + ), + ), + SizedBox( + height: 4, + ), + Container( + padding: EdgeInsets.only(left: 24, right: 25, bottom: 18), + child: Text( + "看了这组照片,网友们也明白了,为什么很多大牌商品喜欢请神仙姐姐去做代言了,因为她只要站在那里,隔着屏幕都能感受到满满的高级感。 别看这次出游,刘亦菲表现得十分欢脱,吃了很多美食...", + style: TextStyle( + fontSize: 14, + height: 1.57, + color: Color(0XFF000000), + fontWeight: FontWeight.w400, + ), + ), + ), + SizedBox(height: 16), + Container( + padding: EdgeInsets.only(left: 24, right: 25, bottom: 18), + child: Row( + children: [ + Expanded( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + const Icon(Icons.photo), + Padding( + padding: const EdgeInsets.only(left: 2), + child: Text( + "最新动态", + style: TextStyle( + fontSize: 14, + color: Colors.black, + fontStyle: FontStyle.normal, + fontWeight: FontWeight.w400, + ), + ), + ), + ], + ), + SizedBox( + height: 14, + ), + Container( + height: 2, + width: 76, + color: Colors.black, + ) + ], + ), + ), + ], + ), + ), + Padding( + padding: EdgeInsets.only(left: 13, right: 11), + child: GridView.count( + shrinkWrap: true, + crossAxisCount: 3, + crossAxisSpacing: 0.0, + mainAxisSpacing: 0.0, + children: List.generate( + images.length, + (index) { + return Image.file(File(images![index])); + + // Container( + // height: 200, + // width: 200, + // decoration: BoxDecoration( + // image: + // DecorationImage( + // image: AssetImage(images[index]), + // fit: BoxFit.cover, + // ), + // ), + // ); + }, + ), + ), + ) + ], + ), + ), + ); + } + + pickBack(String? value) async { + Logs.info('image picked: $value'); + if (value != null) { + String valueCrop = await _cropImage(value); + setState(() { + url = valueCrop; + }); + + FileUploadParam param = FileUploadParam(); + param.files = [MultipartFile.fromFileSync(url)]; + FileUploadRequests.upload(param,(count, total) { }).then((value) => { + + }); + + } else { + setState(() { + url = url == "assets/images/logo128.png" + ? "assets/images/user_null.png" + : "assets/images/logo128.png"; + }); + } + } + + Future _cropImage(String valueCrop) async { + return await ImageCropper.cropImage(context, valueCrop); + } +} diff --git a/study-app-tutorials/lib/view/name.card/name_card3.dart b/study-app-tutorials/lib/view/name.card/name_card3.dart new file mode 100644 index 000000000..423a1e2ca --- /dev/null +++ b/study-app-tutorials/lib/view/name.card/name_card3.dart @@ -0,0 +1,208 @@ +import 'dart:io'; + +import 'package:dio/dio.dart'; +import 'package:flutter/material.dart'; +import 'package:tutorials/component/image.crop/image_cropper.dart'; +import 'package:tutorials/component/log/logs.dart'; +import 'package:tutorials/component/picker/image_picker.dart'; +import 'package:tutorials/locale/translations.dart'; +import 'package:tutorials/request/file_upload_request.dart'; +import 'package:tutorials/request/model/upload/file_upload_param.dart'; +import 'package:tutorials/widgets/profile_icon_basic.dart'; + +class NameCard3 extends StatefulWidget { + const NameCard3({Key? key}) : super(key: key); + + @override + _NameCardState createState() => _NameCardState(); +} + + +class _NameCardState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + body: SingleChildScrollView( + child: Column( + children: [ + SizedBox( + height: 249, + child: Stack( + children: [ + Container( + color: Color(0xffC4C4C4), + height: 186, + ), + Positioned( + right: 121, + bottom: 0, + child: Container( + width: 150, + height: 150, + // margin: EdgeInsets.only( + // top: 99, + // ), + decoration: BoxDecoration( + color: Color(0xff525252), + borderRadius: BorderRadius.circular(150), + ), + ), + ) + ], + ), + ), + SizedBox( + height: 24, + ), + Text( + "John Doe", + style: TextStyle( + fontSize: 24, + color: Colors.black, + fontStyle: FontStyle.normal, + fontWeight: FontWeight.w600, + ), + ), + SizedBox( + height: 12, + ), + Text( + "Designer", + style: TextStyle( + fontSize: 12, + color: Colors.black, + fontStyle: FontStyle.normal, + fontWeight: FontWeight.w400, + ), + ), + SizedBox( + height: 25, + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + GestureDetector( + onTap: () { + Logs.info('GetIt.I.get().back()'); + }, + child: Container( + width: 90, + height: 30, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(30), + border: Border.all(color: Colors.black, width: 1), + ), + child: Center( + child: Text( + "ADD FRIEND", + style: TextStyle( + fontSize: 12, + color: Colors.black, + fontStyle: FontStyle.normal, + fontWeight: FontWeight.w600, + ), + ), + ), + ), + ), + SizedBox(width: 8), + GestureDetector( + onTap: () { + Logs.info('GetIt.I.get().back();'); + }, + child: Container( + width: 90, + height: 30, + decoration: BoxDecoration( + color: Color(0xff262626), + borderRadius: BorderRadius.circular(30), + ), + child: Center( + child: Text( + "FOLLOW", + style: + TextStyle( + fontSize: 12, + color: Colors.white, + fontStyle: FontStyle.normal, + fontWeight: FontWeight.w600, + ), + ), + ), + ), + ), + ], + ), + SizedBox(height: 74), + Padding( + padding: EdgeInsets.symmetric(horizontal: 24), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "ABOUT", + style: TextStyle( + fontSize: 14, + color: Color(0xffA8A8A8), + fontStyle: FontStyle.normal, + fontWeight: FontWeight.w400, + ), + ), + SizedBox( + height: 14, + ), + Text( + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Viverra rutrum elementum nunc velit dui dui, penatibus.", + style: TextStyle( + fontSize: 12, + color: Color(0xffA8A8A8), + fontStyle: FontStyle.normal, + fontWeight: FontWeight.w400, + ), + ) + ], + ), + ), + SizedBox( + height: 30, + ), + Padding( + padding: EdgeInsets.symmetric(horizontal: 24), + child: Align( + alignment: Alignment.centerLeft, + child: Text( + "PHOTOS", + style: TextStyle( + fontSize: 14, + color: Color(0xffA8A8A8), + fontStyle: FontStyle.normal, + fontWeight: FontWeight.w400, + ), + ), + ), + ), + GridView.builder( + shrinkWrap: true, + itemCount: 30, + physics: NeverScrollableScrollPhysics(), + padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12), + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 3, + mainAxisSpacing: 8, + crossAxisSpacing: 6, + ), + itemBuilder: (c, i) { + return Container( + height: 110, + color: Color(0xffC4C4C4), + ); + }, + ) + ], + ), + ), + ); + } +} + diff --git a/study-app-tutorials/lib/view/name.card/name_card_basic.dart b/study-app-tutorials/lib/view/name.card/name_card_basic.dart new file mode 100644 index 000000000..25e688346 --- /dev/null +++ b/study-app-tutorials/lib/view/name.card/name_card_basic.dart @@ -0,0 +1,84 @@ +import 'dart:io'; + +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; +import 'package:tutorials/component/log/logs.dart'; + +/// An example of the elevated card type. +/// +/// The default settings for [Card] will provide an elevated +/// card matching the spec: +/// +/// https://m3.material.io/components/cards/specs#a012d40d-7a5c-4b07-8740-491dec79d58b +class NameCardBasic extends StatefulWidget { + const NameCardBasic( + {Key? key, + this.title = "", + this.subTitle = "", + this.desc = "", + this.url = "assets/images/user_null.png", + this.onTap}) + : super(key: key); + final String title; + final String subTitle; + final String desc; + final String url; + final GestureTapCallback? onTap; + + @override + _NameCardBasicState createState() => _NameCardBasicState(); +} + +class _NameCardBasicState extends State { + @override + Widget build(BuildContext context) { + return Center( + child: Card( + child: Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ListTile( + title: Text(widget.title), + ), + ListTile( + leading: CachedNetworkImage( + imageUrl: widget.url, + placeholder: (context, url) => + const CircularProgressIndicator(), + errorWidget: (context, url, error) => Image.file( + File(url), + width: 150, + height: 150, + ), + height: 86, + width: 86, + ), + title: Text(widget.subTitle), + subtitle: + Text(widget.desc), + ), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + TextButton( + child: const Text('编辑'), + onPressed: widget.onTap, + ), + const SizedBox(width: 8), + TextButton( + child: const Text('分享'), + onPressed: () { + /* ... */ + }, + ), + const SizedBox(width: 8), + ], + ), + ], + ), + ), + ), + ); + } +} diff --git a/study-app-tutorials/lib/view/name.card/name_card_edit.dart b/study-app-tutorials/lib/view/name.card/name_card_edit.dart new file mode 100644 index 000000000..4b5089e12 --- /dev/null +++ b/study-app-tutorials/lib/view/name.card/name_card_edit.dart @@ -0,0 +1,124 @@ +import 'dart:io'; + +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:dio/dio.dart'; +import 'package:flutter/material.dart'; +import 'package:tutorials/component/image.crop/image_cropper.dart'; +import 'package:tutorials/component/log/logs.dart'; +import 'package:tutorials/component/picker/image_picker.dart'; +import 'package:tutorials/locale/translations.dart'; +import 'package:tutorials/request/file_upload_request.dart'; +import 'package:tutorials/request/model/upload/file_upload_param.dart'; +import 'package:tutorials/view/name.card/name_card_basic.dart'; +import 'package:tutorials/view/name.card/name_card_school.dart'; +import 'package:tutorials/widgets/profile_icon_basic.dart'; + +class NameCardEdit extends StatefulWidget { + const NameCardEdit({Key? key}) : super(key: key); + + @override + _NameCardEditState createState() => _NameCardEditState(); +} + +class _NameCardEditState extends State { + String url = "assets/images/user_null.png"; + + @override + Widget build(BuildContext context) { + //获取路由参数 + var args = ModalRoute.of(context)?.settings?.arguments; + print('NameCardEdit args=${args}'); + + return Scaffold( + appBar: AppBar( + title: Text(Translations.textOf(context, "name.card.title")), + ), + body: SafeArea( + child: ListView( + children: [ + NameCardBasic( + title: '学校信息', + subTitle: '河海大学', + desc: '2013年-2017年 计算机科学与技术专业!', + url: 'https://avatars.githubusercontent.com/u/18094768?v=4', + onTap: () {}, + ), + const SizedBox(height: 8), + url.startsWith('assets') + ? ProfileIconBasic( + url: url, + onTap: () { + ImagePicker.pickImage() + .then((value) => {pickBack(value)}); + }, + ) + : GestureDetector( + onTap: () { + ImagePicker.pickImage() + .then((value) => {pickBack(value)}); + }, + child: ClipOval( + child: CachedNetworkImage( + imageUrl: url, + placeholder: (context, url) => + const CircularProgressIndicator(), + errorWidget: (context, url, error) => Image.file( + File(url), + width: 150, + height: 150, + ), + height: 86, + width: 86, + ), + ), + // Image.file( + // File(url), + // width: 150, + // height: 150, + // ), + ), + const SizedBox(height: 16), + Container( + padding: EdgeInsets.only(left: 24, right: 24), + child: Text( + "简介", + style: TextStyle( + fontSize: 16, + color: Color(0xffA8A8A8), + fontWeight: FontWeight.w600, + ), + ), + ), + SizedBox( + height: 4, + ), + ], + ), + ), + ); + } + + pickBack(String? value) async { + Logs.info('image picked: $value'); + if (value != null) { + String valueCrop = await _cropImage(value); + setState(() { + url = valueCrop; + }); + + FileUploadParam param = FileUploadParam(); + param.files = [MultipartFile.fromFileSync(url)]; + FileUploadRequests.upload(param, (count, total) {}).then((value) => {}); + } else { + setState(() { + url = url == "assets/images/logo128.png" + ? "assets/images/user_null.png" + : "assets/images/logo128.png"; + }); + } + } + + Future _cropImage(String valueCrop) async { + return await ImageCropper.cropImage(context, valueCrop); + } +} diff --git a/study-app-tutorials/lib/view/name.card/name_card_school-bk.dart b/study-app-tutorials/lib/view/name.card/name_card_school-bk.dart new file mode 100644 index 000000000..27bf11ef9 --- /dev/null +++ b/study-app-tutorials/lib/view/name.card/name_card_school-bk.dart @@ -0,0 +1,154 @@ +import 'dart:io'; + +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:dio/dio.dart'; +import 'package:flutter/material.dart'; +import 'package:tutorials/component/image.crop/image_cropper.dart'; +import 'package:tutorials/component/log/logs.dart'; +import 'package:tutorials/component/picker/image_picker.dart'; +import 'package:tutorials/locale/translations.dart'; +import 'package:tutorials/request/file_upload_request.dart'; +import 'package:tutorials/request/model/upload/file_upload_param.dart'; +import 'package:tutorials/widgets/profile_icon_basic.dart'; + +class NameCard extends StatefulWidget { + const NameCard({Key? key}) : super(key: key); + + @override + _NameCardState createState() => _NameCardState(); +} + +class _NameCardState extends State { + List images = []; + int _currentIndex = 1; + + String url = "assets/images/user_null.png"; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(Translations.textOf(context, "name.card.title")), + ), + body: SafeArea( + child: ListView( + children: [ + const SizedBox(height: 8), + url.startsWith('assets') + ? ProfileIconBasic( + url: url, + onTap: () { + ImagePicker.pickImage() + .then((value) => {pickBack(value)}); + }, + ) + : GestureDetector( + onTap: () { + ImagePicker.pickImage() + .then((value) => {pickBack(value)}); + }, + child: + ClipOval( + child: CachedNetworkImage( + imageUrl: url, + placeholder: (context, url) => + const CircularProgressIndicator(), + errorWidget: (context, url, error) => Image.file( + File(url), + width: 150, + height: 150, + ), + height: 86, + width: 86, + ), + ), + // Image.file( + // File(url), + // width: 150, + // height: 150, + // ), + ), + const SizedBox(height: 16), + Container( + padding: EdgeInsets.only(left: 24, right: 24), + child: Text( + "简介", + style: TextStyle( + fontSize: 16, + color: Color(0xffA8A8A8), + fontWeight: FontWeight.w600, + ), + ), + ), + SizedBox( + height: 4, + ), + Container( + padding: EdgeInsets.only(left: 24, right: 25, bottom: 18), + child: Text( + "看了这组照片,网友们也明白了,为什么很多大牌商品喜欢请神仙姐姐去做代言了,因为她只要站在那里,隔着屏幕都能感受到满满的高级感。 别看这次出游,刘亦菲表现得十分欢脱,吃了很多美食...", + style: TextStyle( + fontSize: 14, + height: 1.57, + color: Color(0XFF000000), + fontWeight: FontWeight.w400, + ), + ), + ), + SizedBox(height: 16), + + ], + ), + ), + + bottomNavigationBar: BottomNavigationBar( + // 底部导航 + items: [ + BottomNavigationBarItem( + icon: Icon(Icons.favorite_border), + label: Translations.textOf(context, "tv.list.bottomAll")), + BottomNavigationBarItem( + icon: Icon(Icons.favorite), + label: Translations.textOf(context, "tv.list.bottomFavorite")), + ], + currentIndex: _currentIndex, + fixedColor: Colors.blue, + onTap: _onItemTapped, + ), + ); + } + + + void _onItemTapped(int index) { + setState(() { + _currentIndex = index; + }); + } + + pickBack(String? value) async { + Logs.info('image picked: $value'); + if (value != null) { + String valueCrop = await _cropImage(value); + setState(() { + url = valueCrop; + }); + + FileUploadParam param = FileUploadParam(); + param.files = [MultipartFile.fromFileSync(url)]; + FileUploadRequests.upload(param,(count, total) { }).then((value) => { + + }); + + } else { + setState(() { + url = url == "assets/images/logo128.png" + ? "assets/images/user_null.png" + : "assets/images/logo128.png"; + }); + } + } + + Future _cropImage(String valueCrop) async { + return await ImageCropper.cropImage(context, valueCrop); + } +} diff --git a/study-app-tutorials/lib/view/name.card/name_card_school.dart b/study-app-tutorials/lib/view/name.card/name_card_school.dart new file mode 100644 index 000000000..ed6ed6f9b --- /dev/null +++ b/study-app-tutorials/lib/view/name.card/name_card_school.dart @@ -0,0 +1,85 @@ +import 'dart:io'; + +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; +import 'package:tutorials/component/log/logs.dart'; + +/// An example of the elevated card type. +/// +/// The default settings for [Card] will provide an elevated +/// card matching the spec: +/// +/// https://m3.material.io/components/cards/specs#a012d40d-7a5c-4b07-8740-491dec79d58b +class NameCardSchool extends StatefulWidget { + const NameCardSchool( + {Key? key, + this.title = "", + this.url = "assets/images/user_null.png", + this.onTap}) + : super(key: key); + final String title; + final String url; + final GestureTapCallback? onTap; + + @override + _NameCardBasicState createState() => _NameCardBasicState(); +} + +class _NameCardBasicState extends State { + @override + Widget build(BuildContext context) { + return Center( + child: GestureDetector( + onTap: widget.onTap, + child: Card( + child: Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ListTile( + title: Text(widget.title), + ), + ListTile( + leading: CachedNetworkImage( + imageUrl: widget.url, + placeholder: (context, url) => + const CircularProgressIndicator(), + errorWidget: (context, url, error) => Image.file( + File(''), + width: 150, + height: 150, + ), + height: 86, + width: 86, + ), + title: Text('The Enchanted Nightingale'), + subtitle: + Text('Music by Julie Gable. Lyrics by Sidney Stein.'), + ), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + TextButton( + child: const Text('编辑'), + onPressed: () { + Logs.info("bianji clicked..."); + }, + ), + const SizedBox(width: 8), + TextButton( + child: const Text('分享'), + onPressed: () { + /* ... */ + }, + ), + const SizedBox(width: 8), + ], + ), + ], + ), + ), + ), + ), + ); + } +} diff --git a/study-app-tutorials/lib/view/profile/profile.dart b/study-app-tutorials/lib/view/profile/profile.dart new file mode 100644 index 000000000..68b5a3eb2 --- /dev/null +++ b/study-app-tutorials/lib/view/profile/profile.dart @@ -0,0 +1,253 @@ +import 'package:flutter/material.dart'; +import 'package:tutorials/component/log/logs.dart'; +import 'package:tutorials/locale/translations.dart'; + +class Profile extends StatefulWidget { + const Profile({Key? key}) : super(key: key); + + @override + _ProfileState createState() => _ProfileState(); +} + +class _ProfileState extends State { + List images = []; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(Translations.textOf(context, "profile.title")), + ), + body: SafeArea( + child: ListView( + children: [ + SizedBox(height: 8), + Padding( + padding: + EdgeInsets.only(left: 48, right: 168, top: 16, bottom: 8), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + GestureDetector( + onTap: () { + Logs.info("GetIt.I.get().back();"); + }, + child: ClipOval( + child: Image( + image: AssetImage("assets/images/user_null.png")), + ), + ), + Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "刘亦菲", + style: TextStyle( + fontSize: 24, + color: Colors.black, + fontStyle: FontStyle.normal, + fontWeight: FontWeight.w600, + ), + ), + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Icon(Icons.location_on), + Text( + "上海", + style: TextStyle( + fontSize: 14, + color: Colors.black, + fontStyle: FontStyle.normal, + fontWeight: FontWeight.w400, + ), + ), + ]), + ], + ), + ], + ), + ), + SizedBox(height: 16), + Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + "9", + style: TextStyle( + fontSize: 20, + color: Colors.black, + fontStyle: FontStyle.normal, + fontWeight: FontWeight.w600, + ), + ), + Text( + "关注", + style: TextStyle( + fontSize: 14, + color: Colors.black, + fontStyle: FontStyle.normal, + fontWeight: FontWeight.w400, + ), + ), + ], + ), + ), + Expanded( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + "129K", + style: TextStyle( + fontSize: 20, + color: Colors.black, + fontStyle: FontStyle.normal, + fontWeight: FontWeight.w600, + ), + ), + Text( + "粉丝", + style: TextStyle( + fontSize: 14, + color: Colors.black, + fontStyle: FontStyle.normal, + fontWeight: FontWeight.w400, + ), + ), + ], + ), + ), + Expanded( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + "7.2M", + style: TextStyle( + fontSize: 20, + color: Colors.black, + fontStyle: FontStyle.normal, + fontWeight: FontWeight.w600, + ), + ), + Text( + "留言", + style: TextStyle( + fontSize: 14, + color: Colors.black, + fontStyle: FontStyle.normal, + fontWeight: FontWeight.w400, + ), + ), + ], + ), + ) + ], + ), + SizedBox(height: 32), + Container( + padding: EdgeInsets.only(left: 24, right: 24), + child: Text( + "简介", + style: TextStyle( + fontSize: 16, + color: Color(0xffA8A8A8), + fontWeight: FontWeight.w600, + ), + ), + ), + SizedBox( + height: 4, + ), + Container( + padding: EdgeInsets.only(left: 24, right: 25, bottom: 18), + child: Text( + "看了这组照片,网友们也明白了,为什么很多大牌商品喜欢请神仙姐姐去做代言了,因为她只要站在那里,隔着屏幕都能感受到满满的高级感。 别看这次出游,刘亦菲表现得十分欢脱,吃了很多美食...", + style: TextStyle( + fontSize: 14, + height: 1.57, + color: Color(0XFF000000), + fontWeight: FontWeight.w400, + ), + ), + ), + SizedBox(height: 16), + Container( + padding: EdgeInsets.only(left: 24, right: 25, bottom: 18), + child: Row( + children: [ + Expanded( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + const Icon(Icons.photo), + Padding( + padding: const EdgeInsets.only(left: 2), + child: Text( + "最新动态", + style: TextStyle( + fontSize: 14, + color: Colors.black, + fontStyle: FontStyle.normal, + fontWeight: FontWeight.w400, + ), + ), + ), + ], + ), + SizedBox( + height: 14, + ), + Container( + height: 2, + width: 76, + color: Colors.black, + ) + ], + ), + ), + ], + ), + ), + Padding( + padding: EdgeInsets.only(left: 13, right: 11), + child: GridView.count( + shrinkWrap: true, + crossAxisCount: 3, + crossAxisSpacing: 0.0, + mainAxisSpacing: 0.0, + children: List.generate( + images.length, + (index) { + return Container( + height: 122, + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage(images[index]), + fit: BoxFit.cover, + ), + ), + ); + }, + ), + ), + ) + ], + ), + ), + ); + } +} diff --git a/study-app-tutorials/lib/view/settings/dev_tool.dart b/study-app-tutorials/lib/view/settings/dev_tool.dart new file mode 100644 index 000000000..8e7511eb7 --- /dev/null +++ b/study-app-tutorials/lib/view/settings/dev_tool.dart @@ -0,0 +1,36 @@ +import 'package:flutter/material.dart'; +import 'package:tutorials/constant/route_constant.dart'; +import 'package:tutorials/locale/translations.dart'; + +class DevTool extends StatefulWidget { + @override + _DevTool createState() => new _DevTool(); +} + +class _DevTool extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(Translations.textOf(context, "settings.devTool.title")), + ), + body: Container( + alignment: Alignment.center, + margin: EdgeInsets.only(left: 16), + child: Column(children: [ + Container( + alignment: Alignment.center, + child: ListTile( + title: Text('Environment'), + trailing: Icon(Icons.chevron_right), + onTap: (){ + Navigator.of(context).pushNamed( + RouteNameConstant.route_name_env); + }, + ), + ), + ]), + ), + ); + } +} diff --git a/study-app-tutorials/lib/view/settings/env.dart b/study-app-tutorials/lib/view/settings/env.dart new file mode 100644 index 000000000..dc523868d --- /dev/null +++ b/study-app-tutorials/lib/view/settings/env.dart @@ -0,0 +1,158 @@ +import 'package:flutter/material.dart'; +import 'package:tutorials/component/cache/http_request_caches.dart'; +import 'package:tutorials/component/cache/setting_caches.dart'; +import 'package:tutorials/component/dialog/dialogs.dart'; +import 'package:tutorials/component/log/logs.dart'; +import 'package:tutorials/component/sp/shared_preference_helper.dart'; +import 'package:tutorials/component/toast/Toasts.dart'; +import 'package:tutorials/constant/route_constant.dart'; +import 'package:tutorials/constant/sp_constant.dart'; +import 'package:tutorials/locale/translations.dart'; +import 'package:tutorials/utils/app_utils.dart'; + +class Environment extends StatefulWidget { + @override + _Environment createState() => new _Environment(); +} + +class _Environment extends State { + static List _list = []; + + int groupValue = 1; + String mock_switch = "false"; + + @override + void initState() { + super.initState(); + initData(); + } + + Future initData() async { + groupValue = int.parse(HttpRequestCaches.getIndex()); + _list = []; + _list.add(FMRadioModel(1, "Development")); + _list.add(FMRadioModel(2, "Test")); + _list.add(FMRadioModel(3, "Production")); + + mock_switch = await SettingCaches.getMockSwitch(); + + setState(() {}); + } + + @override + Widget build(BuildContext context) { + _list[0] = (FMRadioModel(1, Translations.textOf(context, "env.dev"))); + _list[1] = (FMRadioModel(2, Translations.textOf(context, "env.test"))); + _list[2] = (FMRadioModel(3, Translations.textOf(context, "env.prod"))); + + return Scaffold( + appBar: AppBar( + title: Text(Translations.textOf(context, "env.title")), + ), + body: Container( + alignment: Alignment.center, + child: Column(children: [ + Container( + padding: const EdgeInsets.fromLTRB(20.0, 40.0, 0.0, 0.0), + child: Row( + children: [ + Text( + 'Mockbale', + style: TextStyle( + fontSize: 20, + color: Colors.black, + fontStyle: FontStyle.normal, + fontWeight: FontWeight.w400, + ), + ), + Switch( + // This bool value toggles the switch. + value: mock_switch == "true", + activeColor: Colors.blue, + onChanged: (bool value) { + mock_switch = value ? "true" : "false"; + // This is called when the user toggles the switch. + SettingCaches.cacheMockSwitch(value ? "true" : "false") + .then((result) { + setState(() { + Toasts.show(result + ? Translations.textOf(context, "all.save.success") + : Translations.textOf(context, "all.save.failure")); + }); + }); + }, + ), + ], + ), + ), + Container( + padding: const EdgeInsets.fromLTRB(0.0, 10.0, 0.0, 0.0), + child: Text( + 'Env', + style: TextStyle( + fontSize: 20, + color: Colors.black, + fontStyle: FontStyle.normal, + fontWeight: FontWeight.w400, + ), + ), + ), + + Expanded( + child: ListView.builder( + itemCount: _list.length, + itemBuilder: (context, index) { + FMRadioModel model = _list[index]; + return _buildRow(model); + }, + ), + ), + ]), + ), + ); + } + + Row _buildRow(FMRadioModel model) { + return Row( + children: [ + _radio(model), + GestureDetector( + child: Text(model.text), + onTap: () { + go(model.index); + }, + ), + ], + ); + } + + Radio _radio(FMRadioModel model) { + return Radio( + value: model.index, + groupValue: groupValue, + onChanged: (index) { + groupValue = index; + Logs.info('index : ${index}'); + HttpRequestCaches.setIndex(index.toString()).then((value) { + setState(() { + Toasts.show(value + ? Translations.textOf(context, "all.save.success") + : Translations.textOf(context, "all.save.failure")); + }); + }); + }); + } + + void go(int index) { + String str = index.toString(); + Logs.info("json : $str"); + AppUtils.toPage(context, RouteNameConstant.route_name_env_edit, args: str); + } +} + +class FMRadioModel extends Object { + int index; + String text; + + FMRadioModel(this.index, this.text); +} diff --git a/study-app-tutorials/lib/view/settings/env_edit.dart b/study-app-tutorials/lib/view/settings/env_edit.dart new file mode 100644 index 000000000..7129e7a9d --- /dev/null +++ b/study-app-tutorials/lib/view/settings/env_edit.dart @@ -0,0 +1,142 @@ +import 'package:flutter/material.dart'; +import 'package:tutorials/component/cache/http_request_caches.dart'; +import 'package:tutorials/component/dialog/dialogs.dart'; +import 'package:tutorials/component/log/logs.dart'; +import 'package:tutorials/component/sp/shared_preference_helper.dart'; +import 'package:tutorials/constant/sp_constant.dart'; +import 'package:tutorials/locale/translations.dart'; +import 'package:tutorials/utils/app_utils.dart'; + +class EnvironmentEdit extends StatefulWidget { + @override + _EnvironmentEdit createState() => new _EnvironmentEdit(); +} + +class _EnvironmentEdit extends State { + String _protocol = ""; + String _host = ""; + String _port = ""; + String _index = ""; + + @override + Widget build(BuildContext context) { + String str = AppUtils.getArgs(context).toString(); + Logs.info("param index : $str"); + _index = str; + initValues(); + + return Scaffold( + appBar: AppBar( + title: Text(Translations.textOf(context, "env.edit.title")), + ), + body: Container( + alignment: Alignment.center, + margin: EdgeInsets.only(left: 16), + child: Column(children: [ + Container( + child: GestureDetector( + onTap: () { + List list = []; + list.add("http"); + list.add("https"); + Dialogs.showListBottomSheet(context, list).then((value) { + if(value == null){ + return ; + } + HttpRequestCaches.setProtocol( + list[value ?? 0], _index); + initValues(); + }); + }, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(Translations.textOf( + context, "env.edit.protocol")), + Text('${_protocol}', + style: TextStyle(fontWeight: FontWeight.bold)), + IconButton( + icon: Icon( + Icons.chevron_right, + ), + onPressed: () {}, + ), + ], + ), + ), + ), + Container( + child: GestureDetector( + onTap: () { + Dialogs.showInputDialog( + context, + Translations.textOf(context, "env.edit.host"), + '${_host}') + .then((value) { + if(value == null || value == ""){ + return ; + } + HttpRequestCaches.setHost(value ?? "", _index); + initValues(); + }); + }, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(Translations.textOf(context, "env.edit.host")), + Text('${_host}', + style: TextStyle(fontWeight: FontWeight.bold)), + IconButton( + icon: Icon( + Icons.chevron_right, + ), + onPressed: () {}, + ), + ], + ), + ), + ), + Container( + child: GestureDetector( + onTap: () { + Dialogs.showInputDialog( + context, + Translations.textOf(context, "env.edit.port"), + '${_port}') + .then((value) { + if(value == null || value == ""){ + return ; + } + HttpRequestCaches.setPort(value ?? "0", _index); + initValues(); + }); + }, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(Translations.textOf(context, "env.edit.port")), + Text('${_port}', + style: TextStyle(fontWeight: FontWeight.bold)), + IconButton( + icon: Icon( + Icons.chevron_right, + ), + onPressed: () => {}, + ), + ], + ), + ), + ), + ]), + ), + ); + } + + void initValues() { + setState(() { + _protocol = HttpRequestCaches.getIndexProtocol(_index); + _host = HttpRequestCaches.getIndexHost(_index); + _port = HttpRequestCaches.getIndexPort(_index); + }); + } +} diff --git a/study-app-tutorials/lib/view/settings/feedback_tab_view.dart b/study-app-tutorials/lib/view/settings/feedback_tab_view.dart new file mode 100644 index 000000000..e92cc35b3 --- /dev/null +++ b/study-app-tutorials/lib/view/settings/feedback_tab_view.dart @@ -0,0 +1,357 @@ + +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:tutorials/component/cache/user_caches.dart'; +import 'package:tutorials/component/dialog/dialogs.dart'; +import 'package:tutorials/component/event/event_bus.dart'; +import 'package:tutorials/component/log/logs.dart'; +import 'package:tutorials/component/toast/Toasts.dart'; +import 'package:tutorials/constant/error_code_constant.dart'; +import 'package:tutorials/constant/locale_constant.dart'; +import 'package:tutorials/locale/translations.dart'; +import 'package:tutorials/model/about_author_tab.dart'; +import 'package:tutorials/request/feedback_request.dart'; +import 'package:tutorials/request/model/feedback/message_feedback_delete_request_param.dart'; +import 'package:tutorials/request/model/feedback/message_feedback_query_request_param.dart'; +import 'package:tutorials/request/model/feedback/message_feedback.dart'; +import 'package:tutorials/view/settings/feedback_update_event.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +class FeedbackTabView extends StatelessWidget { + FeedbackTabView({Key? key, required this.tab, required this.feedbacks}) + : super(key: key); + + TabTitle tab; + List feedbacks; + static const String key_all = 'all'; + static const String key_my = 'my'; + + static List init(String all,String me) { + List tabs = []; + tabs.add(TabTitle(key_all, all)); + tabs.add(TabTitle(key_my, me)); + return tabs; + } + + @override + Widget build(BuildContext context) { + switch (tab.key) { + case key_all: + return _FeedbackTabView(false); + case key_my: + return _FeedbackTabView(true); + default: + return const Text(''); + } + } +} + +class _FeedbackTabView extends StatefulWidget { + late bool _isMe; + + _FeedbackTabView(bool isMe) { + _isMe = isMe; + } + + @override + _FeedbackTabState createState() => _FeedbackTabState(_isMe); +} + +class _FeedbackTabState extends State<_FeedbackTabView> { + List list = []; + bool _isLoading = false; + bool _isMe = false; + + _FeedbackTabState(bool isMe) { + _isMe = isMe; + } + + @override + void initState() { + super.initState(); + showLoading(); + _onRefresh(); + EventBus.consume((event) { + Logs.info('event = ${event.toString()}'); + _onRefresh(); + }); + } + + @override + Widget build(BuildContext context) { + return RefreshIndicator( + onRefresh: _onRefresh, + child: Container( + child: _isLoading + ? Center( + child: CircularProgressIndicator( + backgroundColor: Colors.grey[200], + valueColor: const AlwaysStoppedAnimation(Colors.blue), + )) + : (list.isEmpty + ? Center( + child: Text( + Translations.textOf(context, "all.list.view.no.data"), + textAlign: TextAlign.center, + ), + ) + : ListView.separated( + itemCount: list.length, + shrinkWrap: true, + itemBuilder: (context, index) { + MessageFeedback appFeedback = list[index]; + return GestureDetector( + onTap: () {}, + child: Card( + clipBehavior: Clip.antiAlias, + elevation: 2, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8.0)), + margin: const EdgeInsets.symmetric( + horizontal: 16, vertical: 6), + child: Container( + child: Row( + children: [ + Container( + padding: const EdgeInsets.only( + left: 0, right: 0), + width: 64.0, + child: ClipOval( + child: CachedNetworkImage( + imageUrl: + appFeedback.fromUserIconUrl ?? '', + placeholder: (context, url) => + const CircularProgressIndicator(), + errorWidget: (context, url, error) => + const Image( + image: AssetImage( + "assets/images/user_null.png")), + height: 64, + width: 64, + ), + ), + ), + Expanded( + flex: 1, + child: Container( + padding: const EdgeInsets.symmetric( + horizontal: 5, vertical: 10), + color: Colors.white, + child: Column( + children: [ + Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Container( + child: Text( + appFeedback + .fromUserName ?? + '佚名', + overflow: TextOverflow + .ellipsis, + style: + const TextStyle( + color: Colors + .black, + fontSize: + 15)), + ), + ], + )), + _isMe + ? Expanded( + child: Column( + crossAxisAlignment: + CrossAxisAlignment + .end, + children: [ + GestureDetector( + onTap: () { + _delete( + appFeedback); + }, + child: + const Icon( + Icons + .delete, + color: Colors + .grey, + size: 20.0, + )) + ]), + ) + : SizedBox.shrink(), + ], + ), + const SizedBox( + height: 8, + ), + Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Container( + child: Text( + appFeedback + .msgContent ?? + '', + maxLines: 3, + style: const TextStyle( + fontSize: 14, + color: + Colors.black87), + overflow: TextOverflow + .ellipsis, + ), + ), + ], + )), + ], + ), + Row( + children: [ + // Expanded( + // child: Container( + // margin: EdgeInsets.only( + // top: 8.0, + // right: 0.0), + // child: Row( + // crossAxisAlignment: + // CrossAxisAlignment + // .start, + // children: [ + // Icon( + // Icons.thumb_up, + // color: + // Colors.grey, + // size: 12.0, + // ), + // Text( + // " 100 " + + // '\t\t\t', + // style: TextStyle( + // fontSize: + // 12, + // color: Colors + // .grey)), + // Icon( + // Icons + // .thumb_down, + // color: + // Colors.grey, + // size: 12.0, + // ), + // Text(" 5 ", + // style: TextStyle( + // fontSize: + // 12, + // color: Colors + // .grey)), + // ]))), + Expanded( + child: Column( + crossAxisAlignment: + CrossAxisAlignment.end, + children: [ + Container( + margin: + const EdgeInsets.only( + top: 8.0, + right: 8.0), + child: Text( + '${appFeedback.sendTime}', + style: const TextStyle( + fontSize: 12, + color: Colors.grey), + ), + ), + ], + )), + ], + ), + ], + )), + ), + ], + ), + ), + )); + }, + separatorBuilder: (context, index) => Divider(height: .0), + )), + ), + ); + } + + Future _onRefresh() async { + if (!mounted) { + return; + } + + MessageFeedbackQueryRequestParam param = MessageFeedbackQueryRequestParam(); + if (_isMe) { + param.fromUserId = UserCaches.getUserId(); + } + MessageFeedbackRequests.query(param).then((result) { + hideLoading(); + Logs.info('_onRefresh responseBody=' + (result.toString() ?? "")); + if (result.common.code == ErrorCodeConstant.success) { + Logs.info('_onRefresh length = ${result.feedbacks.length}'); + setState(() { + list = result.feedbacks ?? []; + }); + } else { + Toasts.show(result.common.message); + } + }).catchError((error) { + Logs.info(error.toString()); + hideLoading(); + }); + } + + Future _delete(MessageFeedback appFeedback) async { + // 确认框 + String? showConfirmDialog = await Dialogs.showConfirmDialog(context, + Translations.textOf(context, LocaleConstant.all_delete_confirm), null); + if ("true" == showConfirmDialog) { + showLoading(); + + MessageFeedbackDeleteRequestParam param = + MessageFeedbackDeleteRequestParam(); + param.feedbackId = appFeedback.id; + Logs.info('_delete feedbackId=' + (param.feedbackId.toString() ?? "")); + + MessageFeedbackRequests.delete(param).then((result) { + hideLoading(); + Logs.info('_delete responseBody=' + (result.toString() ?? "")); + if (result.common.code == ErrorCodeConstant.success) { + _onRefresh(); + } else { + Toasts.show(result.common.message); + } + }).catchError((error) { + Logs.info(error.toString()); + hideLoading(); + }); + } + } + + hideLoading() { + setState(() { + _isLoading = false; + }); + } + + showLoading() { + setState(() { + _isLoading = true; + }); + } +} diff --git a/study-app-tutorials/lib/view/settings/feedback_update_event.dart b/study-app-tutorials/lib/view/settings/feedback_update_event.dart new file mode 100644 index 000000000..7b5fe9226 --- /dev/null +++ b/study-app-tutorials/lib/view/settings/feedback_update_event.dart @@ -0,0 +1,5 @@ +import 'package:tutorials/component/event/base_event.dart'; + +class FeedbackUpdateEvent extends BaseEvent { + String data = ""; +} diff --git a/study-app-tutorials/lib/view/settings/feedbacks.dart b/study-app-tutorials/lib/view/settings/feedbacks.dart new file mode 100644 index 000000000..f5b577346 --- /dev/null +++ b/study-app-tutorials/lib/view/settings/feedbacks.dart @@ -0,0 +1,108 @@ +import 'package:tutorials/component/cache/user_caches.dart'; +import 'package:tutorials/component/dialog/dialogs.dart'; +import 'package:tutorials/component/event/event_bus.dart'; +import 'package:tutorials/component/log/logs.dart'; +import 'package:tutorials/component/toast/Toasts.dart'; +import 'package:tutorials/constant/error_code_constant.dart'; +import 'package:tutorials/locale/translations.dart'; +import 'package:tutorials/model/about_author_tab.dart'; +import 'package:tutorials/request/feedback_request.dart'; +import 'package:tutorials/request/model/feedback/message_feedback.dart'; +import 'package:tutorials/request/model/feedback/message_feedback_insert_request_param.dart'; +import 'package:tutorials/utils/date_time_utils.dart'; +import 'package:tutorials/view/settings/feedback_update_event.dart'; +import 'package:flutter/material.dart'; + +import 'feedback_tab_view.dart'; + +class Feedbacks extends StatefulWidget { + @override + _Feedbacks createState() => _Feedbacks(); +} + +class _Feedbacks extends State with SingleTickerProviderStateMixin { + late TabController _tabController; //需要定义一个Controller + List tabs = []; + + Future _insertFeedback() async { + String msgContent = await Dialogs.showInputBottomSheet(context, + Translations.textOf(context, 'settings.feedback.input.hint'), "") ?? + ""; + if (msgContent.isEmpty) { + return; + } + + MessageFeedback feedback = MessageFeedback(); + feedback.fromUserId = UserCaches.getUserId(); + feedback.fromUserIconUrl = UserCaches.getUser().iconUrl; + feedback.msgContent = msgContent; + feedback.sendTime = DateTimeUtils.format(DateTime.now()); + + MessageFeedbackInsertRequestParam param = + MessageFeedbackInsertRequestParam(); + param.feedback = feedback; + MessageFeedbackRequests.insert(param).then((result) { + Logs.info('_insertFeedback responseBody=' + (result?.toString() ?? "")); + if (result.common.code == ErrorCodeConstant.success) { + setState(() { + FeedbackUpdateEvent event = FeedbackUpdateEvent(); + event.data = result.common.message; + EventBus.publish(event); + }); + } else { + Toasts.show(result.common.message); + } + }).catchError((error) { + Logs.info(error.toString()); + }); + } + + @override + Widget build(BuildContext context) { + String all = Translations.textOf(context, 'settings.feedback.all'); + String my = Translations.textOf(context, 'settings.feedback.my'); + + tabs = FeedbackTabView.init(all, my); + _tabController = TabController(length: tabs.length, vsync: this); + + + return Scaffold( + appBar: AppBar( + title: Text(Translations.textOf(context, "settings.feedbacks")), + bottom: TabBar( + //生成Tab菜单 + controller: _tabController, + tabs: tabs + .map((e) => Tab( + text: e.text, + )) + .toList()), + ), + body: Container( + alignment: Alignment.center, + child: Column(children: [ + Expanded( + child: Container( + child: TabBarView( + controller: _tabController, + children: tabs.map((e) { + return Container( + child: FeedbackTabView( + tab: e, + feedbacks: [], + ), + ); + }).toList(), + ), + ), + ), + ]), + ), + floatingActionButton: FloatingActionButton( + onPressed: _insertFeedback, + tooltip: '+', + child: const Icon(Icons.add), + ), + ); + } +} diff --git a/study-app-tutorials/lib/view/settings/settings.dart b/study-app-tutorials/lib/view/settings/settings.dart new file mode 100644 index 000000000..d79c7e123 --- /dev/null +++ b/study-app-tutorials/lib/view/settings/settings.dart @@ -0,0 +1,355 @@ +import 'dart:io'; + +import 'package:tutorials/component/cache/setting_caches.dart'; +import 'package:tutorials/component/event/event_bus.dart'; +import 'package:tutorials/component/picker/file_picker.dart'; +import 'package:tutorials/component/toast/Toasts.dart'; +import 'package:tutorials/constant/error_code_constant.dart'; +import 'package:tutorials/locale/supported_locales.dart'; +import 'package:tutorials/model/app_version.dart'; +import 'package:flutter/material.dart'; +import 'package:tutorials/component/dialog/dialogs.dart'; +import 'package:tutorials/component/http/cancel_requests.dart'; +import 'package:tutorials/component/http/http_byte_result.dart'; +import 'package:tutorials/component/http/http_requests.dart'; +import 'package:tutorials/component/log/logs.dart'; +import 'package:tutorials/constant/locale_constant.dart'; +import 'package:tutorials/constant/route_constant.dart'; +import 'package:tutorials/locale/translations.dart'; +import 'package:tutorials/request/model/setting/app_version_check_request_param.dart'; +import 'package:tutorials/request/model/setting/app_version_check_request_result.dart'; +import 'package:tutorials/request/setting_request.dart'; +import 'package:tutorials/utils/app_utils.dart'; +import 'package:tutorials/utils/file_utils.dart'; +import 'package:tutorials/view/home/locale_update_event.dart'; +import 'package:url_launcher/url_launcher.dart'; + +class Settings extends StatefulWidget { + GlobalKey<_Settings> changeLocalizationStateKey = new GlobalKey<_Settings>(); + @override + _Settings createState() => new _Settings(); +} + +class _Settings extends State { + late BuildContext _context; + bool _isLoading = false; + double? _value; + CancelRequests cancelRequests = CancelRequests(); + late AppVersion _currentVersionInfo; + + @override + void initState() { + _currentVersionInfo = AppVersion.getCurrentVersionInfo(); + } + + @override + Widget build(BuildContext context) { + _context = context; + return Stack( + alignment: Alignment.center, //指定未定位或部分定位widget的对齐方式 + children: [ + Scaffold( + appBar: AppBar( + title: Text(Translations.textOf(context, "settings.title")), + ), + body: Container( + alignment: Alignment.center, + child: Column(children: [ + Container( + alignment: Alignment.center, + padding: const EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 0.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + GestureDetector( + child: const Image( + image: AssetImage("assets/images/logo128.png")), + onDoubleTap: () { + Navigator.of(context).pushNamed( + RouteNameConstant.route_name_setting_dev_tool); + }, + ), + ], + ), + ), + Container( + alignment: Alignment.center, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text(Translations.textOf(context, "all.app.name"), + style: const TextStyle( + fontSize: 17.0, fontWeight: FontWeight.bold)) + ], + ), + ), + Container( + alignment: Alignment.center, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text(_currentVersionInfo?.versionName ?? '') + ], + ), + ), + Container( + alignment: Alignment.center, + child: ListTile( + title: Text( + Translations.textOf(context, "settings.checkForUpdate")), + trailing: Icon(Icons.chevron_right), + onTap: _checkAppUpdateInfo, + ), + ), + Container( + alignment: Alignment.center, + child: ListTile( + title: + Text(Translations.textOf(context, "settings.feedbacks")), + trailing: const Icon(Icons.chevron_right), + onTap: () { + Navigator.of(context).pushNamed( + RouteNameConstant.route_name_setting_feedbacks); + }, + ), + ), + Container( + alignment: Alignment.center, + child: ListTile( + title: Text( + Translations.textOf(context, "settings.dir.setting")), + trailing: const Icon(Icons.chevron_right), + onTap: () { + downloadDirSetting(); + }, + ), + ), + Container( + alignment: Alignment.center, + child: ListTile( + title: Text( + Translations.textOf(context, "settings.lang.setting")), + trailing: const Icon(Icons.chevron_right), + onTap: () { + langSetting(); + }, + ), + ), + Container( + alignment: Alignment.center, + child: ListTile( + title: Text( + Translations.textOf(context, "settings.name.card")), + trailing: Icon(Icons.chevron_right), + onTap: () { + _nameCard(); + }, + ), + ), + + ]), + ), + ), + Container( + child: _isLoading + ? Container( + color: Colors.black54.withOpacity(0.5), + width: double.infinity, + ) + : null, + ), + Container( + child: _isLoading + ? CircularProgressIndicator( + value: _value, + backgroundColor: Colors.grey[200], + valueColor: const AlwaysStoppedAnimation(Colors.blue), + ) + : null, + ), + ], + ); + } + + Future onWillPop() async { + // 确认框 + String? showConfirmDialog = await Dialogs.showConfirmDialog( + context, + Translations.textOf(context, LocaleConstant.settings_upgrade_cancel), + null); + Logs.info('showConfirmDialog = $showConfirmDialog'); + if ("true" == showConfirmDialog) { + cancelRequests.cancel('_onWillPop'); + return true; + } + // You can do some work here. + // Returning true allows the pop to happen, returning false prevents it. + return false; + } + + showLoading({double? value}) { + setState(() { + _value = value; + _isLoading = true; + }); + } + + hideLoading() { + setState(() { + _value = null; + _isLoading = false; + }); + } + + void _checkAppUpdateInfo() async { + showLoading(); + // 版本校验 + SettingRequests.checkVersion(AppVersionCheckRequestParam()).then((result) { + Logs.info('checkVersion result=' + (result.toString() ?? "")); + hideLoading(); + if (result.common.code == ErrorCodeConstant.success) { + showUpdateDialog(result); + } else { + Toasts.show(result.common.message); + } + }).catchError((error) { + Logs.info(error.toString()); + hideLoading(); + }); + } + + void showUpdateDialog(AppVersionCheckRequestResult result) async { + // 确认框 + String? showConfirmDialog = await Dialogs.showConfirmDialog( + context, + Translations.textOf(context, LocaleConstant.settings_upgrade_dialog), + result?.description?.replaceAll('|', '\n')); + Logs.info('showConfirmDialog = $showConfirmDialog'); + + if ("true" == showConfirmDialog) { + showUpdateSelector(result); + } + } + + void showUpdateSelector(AppVersionCheckRequestResult result) async { + List contents = [ + Translations.textOf(context, "settings.upgrade.open.by.browser"), + Translations.textOf(context, "settings.upgrade.open.by.app"), + Translations.textOf(context, "settings.upgrade.open.cancel") + ]; + int? index = await Dialogs.showButtonSelectDialog(context, contents, null); + Logs.info('select index = $index'); + + if (index == null || index == contents.length - 1) { + // Cancel + return; + } + + if (index == 0) { + // open with browser + String url = HttpRequests.rebuildUrl(result?.fileUrl ?? ""); + if (await canLaunch(url)) { + await launch(url); + return; + } + } else if (index == 1) { + // download from app + downloadFromApp(result); + } + } + + void downloadFromApp(AppVersionCheckRequestResult result) async { + String downloadDir = await FileUtils.getDownloadDirectory(); + String fileName = '${downloadDir}/latest-app-${result?.versionCode}.apk'; + File file = File(fileName); + if (file.existsSync()) { + // Logs.info('delete is ${file.path}'); + // file.delete(); + // 已经下载过,直接安装 + Logs.info('open file ${file.path}'); + FileUtils.openFile(file); + return; + } + + Logs.info('cancelRequests is ${cancelRequests.token}'); + showLoading(value: _value); + // 下载 + HttpByteResult httpByteResult = await HttpRequests.getBytes( + result?.fileUrl ?? "", null, null, (int sent, int total) { + setState(() { + _value = sent / total; + }); + Logs.info("_doDownloadRequest onReceiveProgress $sent / $total"); + Logs.info("_doDownloadRequest onReceiveProgress value = $_value"); + }, cancelRequests); + + // 保存 + if (httpByteResult.responseBytes == null) { + return; + } + Logs.info('download apk finished...'); + if (httpByteResult.responseBytes.isEmpty) { + Dialogs.dismiss(_context); + Dialogs.showInfoDialog(_context, null, + Translations.textOf(context, "settings.alreadyLatestVersion")); + return; + } + + bool isSuccess = await FileUtils.write(file, httpByteResult.responseBytes); + Logs.info('save file isSuccess = ${isSuccess} to ${file.path}'); + + hideLoading(); + + // 打开文件 + if (isSuccess) { + FileUtils.openFile(file); + Logs.info('open file ${file.path}'); + } + } + + Future downloadDirSetting() async { + String? dir = await FilePicker.pickDirectory(); + if (dir != null) { + SettingCaches.cacheDownloadDirectory(dir); + Toasts.show(Translations.textOf( + context, "settings.download.dir.setting.success")); + } + } + + void langSetting() async { + List contents = [ + Translations.textOf(context, "settings.lang.zh"), + Translations.textOf(context, "settings.lang.en"), + Translations.textOf(context, "settings.lang.cancel") + ]; + // 确认框 + int? index = await Dialogs.showButtonSelectDialog(context, contents, null); + Logs.info('select index = $index'); + + if (index == null || index == contents.length - 1) { + // Cancel + return; + } + + if (index == 0) { + // zh + Translations.load(SupportedLocales.zhLocale); + } else if (index == 1) { + // en + Translations.load(SupportedLocales.enLocale); + } + + LocaleUpdateEvent event = LocaleUpdateEvent(); + event.data = index.toString(); + EventBus.publish(event); + + setState(() { + Toasts.show('Success'); + }); + } + + + Future _nameCard() async { + AppUtils.toPage(context, RouteNameConstant.route_name_name_card); + } +} diff --git a/study-app-tutorials/lib/widgets/app_bar_only_back.dart b/study-app-tutorials/lib/widgets/app_bar_only_back.dart new file mode 100644 index 000000000..934a204a5 --- /dev/null +++ b/study-app-tutorials/lib/widgets/app_bar_only_back.dart @@ -0,0 +1,16 @@ +import 'package:flutter/material.dart'; +import 'package:tutorials/constant/color_constant.dart'; + +class AppBarOnlyBack extends AppBar { + + AppBarOnlyBack(); + + @override + Widget build(BuildContext context) { + return AppBar( + backgroundColor: Colors.transparent, + elevation: 0, + iconTheme: IconThemeData(color: ColorConstant.app_bar_only_back_color), + ); + } +} diff --git a/study-app-tutorials/lib/widgets/common_divider.dart b/study-app-tutorials/lib/widgets/common_divider.dart new file mode 100644 index 000000000..cc5f23208 --- /dev/null +++ b/study-app-tutorials/lib/widgets/common_divider.dart @@ -0,0 +1,30 @@ +import 'package:flutter/material.dart'; + +class ProfileTile extends StatelessWidget { + final title; + final subtitle; + final textColor; + ProfileTile({this.title, this.subtitle, this.textColor = Colors.black}); + @override + Widget build(BuildContext context) { + return Column( + // crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + title, + style: TextStyle( + fontSize: 20.0, fontWeight: FontWeight.w700, color: textColor), + ), + SizedBox( + height: 5.0, + ), + Text( + subtitle, + style: TextStyle( + fontSize: 15.0, fontWeight: FontWeight.normal, color: textColor), + ), + ], + ); + } +} \ No newline at end of file diff --git a/study-app-tutorials/lib/widgets/main_wrapper.dart b/study-app-tutorials/lib/widgets/main_wrapper.dart new file mode 100644 index 000000000..a71a3089f --- /dev/null +++ b/study-app-tutorials/lib/widgets/main_wrapper.dart @@ -0,0 +1,57 @@ +import 'package:flutter/material.dart'; + +class MainViewWrapper extends StatefulWidget { + late Widget _child; + late bool _isLoading; + + MainViewWrapper(Widget child,bool isLoading, {Key? key}) : super(key: key) { + _child = child; + _isLoading = isLoading; + } + + @override + State createState() => + _MainViewWrapperState(_child, _isLoading); + +} + +class _MainViewWrapperState extends State { + Widget _child = const SizedBox(); + bool _isLoading = false; + + _MainViewWrapperState(Widget child, bool isLoading) { + _child = child; + _isLoading = isLoading; + } + + @override + Widget build(BuildContext context) { + return Stack( + alignment: Alignment.center, //指定未定位或部分定位widget的对齐方式 + children: [ + Scaffold( + backgroundColor: const Color(0xFFFCFCFC), + body: SafeArea( + child: _child, + ), + ), + Container( + child: _isLoading + ? Container( + color: Colors.black54.withOpacity(0.5), + width: double.infinity, + ) + : null, + ), + Container( + child: _isLoading + ? CircularProgressIndicator( + backgroundColor: Colors.grey[200], + valueColor: const AlwaysStoppedAnimation(Colors.blue), + ) + : null, + ), + ], + ); + } +} diff --git a/study-app-tutorials/lib/widgets/profile_icon_basic.dart b/study-app-tutorials/lib/widgets/profile_icon_basic.dart new file mode 100644 index 000000000..7d2724bee --- /dev/null +++ b/study-app-tutorials/lib/widgets/profile_icon_basic.dart @@ -0,0 +1,26 @@ +import 'package:flutter/material.dart'; + +class ProfileIconBasic extends StatefulWidget { + const ProfileIconBasic( + {Key? key, this.url = "assets/images/user_null.png", this.onTap}) + : super(key: key); + final String url; + final GestureTapCallback? onTap; + + @override + _ProfileIconBasicState createState() => _ProfileIconBasicState(); +} + +class _ProfileIconBasicState extends State { + @override + Widget build(BuildContext context) { + return Center( + child: GestureDetector( + onTap: widget.onTap, + child: ClipOval( + child: Image(image: AssetImage(widget.url)), + ), + ), + ); + } +} diff --git a/study-app-tutorials/lib/widgets/profile_tile.dart b/study-app-tutorials/lib/widgets/profile_tile.dart new file mode 100644 index 000000000..84917b8e9 --- /dev/null +++ b/study-app-tutorials/lib/widgets/profile_tile.dart @@ -0,0 +1,11 @@ +import 'package:flutter/material.dart'; + +class CommonDivider extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Divider( + color: Colors.grey.shade300, + height: 8.0, + ); + } +} \ No newline at end of file diff --git a/study-app-tutorials/lib/widgets/scaffo_id_app_bar_only_back.dart b/study-app-tutorials/lib/widgets/scaffo_id_app_bar_only_back.dart new file mode 100644 index 000000000..b779154a7 --- /dev/null +++ b/study-app-tutorials/lib/widgets/scaffo_id_app_bar_only_back.dart @@ -0,0 +1,16 @@ +import 'package:flutter/material.dart'; + +class AppBarOnlyBack extends AppBar { + final Color color = Colors.black; + + AppBarOnlyBack(); + + @override + AppBar build(BuildContext context) { + return AppBar( + backgroundColor: Colors.transparent, + elevation: 0, + iconTheme: IconThemeData(color: color), + ); + } +} diff --git a/study-app-tutorials/lib/widgets/verification_text_field.dart b/study-app-tutorials/lib/widgets/verification_text_field.dart new file mode 100644 index 000000000..b6daf7823 --- /dev/null +++ b/study-app-tutorials/lib/widgets/verification_text_field.dart @@ -0,0 +1,49 @@ + + +import 'package:flutter/material.dart'; + +class VerificationTextField extends StatefulWidget { + late TextEditingController _codeController; + + VerificationTextField(TextEditingController codeController, {Key? key}) + : super(key: key) { + _codeController = codeController; + } + + @override + State createState() => _VerificationTextFieldState(); +} + +class _VerificationTextFieldState extends State { + @override + Widget build(BuildContext context) { + return Container( + width: 64, + height: 64, + padding: const EdgeInsets.symmetric(horizontal: 5), + decoration: BoxDecoration( + color: const Color(0xFFF4F4F4), + borderRadius: BorderRadius.circular(4), + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.6), + spreadRadius: 2, + blurRadius: 2, + offset: Offset.zero, + ), + ], + ), + child: TextField( + controller: widget._codeController, + textAlign: TextAlign.center, + maxLength: 1, + style: const TextStyle( + fontSize: 28, + ), + decoration: const InputDecoration( + counterText: "", + ), + ), + ); + } +} \ No newline at end of file diff --git a/study-app-tutorials/pubspec.lock b/study-app-tutorials/pubspec.lock new file mode 100644 index 000000000..733109c7e --- /dev/null +++ b/study-app-tutorials/pubspec.lock @@ -0,0 +1,724 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + async: + dependency: transitive + description: + name: async + url: "https://pub.dartlang.org" + source: hosted + version: "2.8.1" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + cached_network_image: + dependency: "direct main" + description: + name: cached_network_image + url: "https://pub.dartlang.org" + source: hosted + version: "3.2.0" + cached_network_image_platform_interface: + dependency: transitive + description: + name: cached_network_image_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" + cached_network_image_web: + dependency: transitive + description: + name: cached_network_image_web + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + characters: + dependency: transitive + description: + name: characters + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + charcode: + dependency: transitive + description: + name: charcode + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.1" + clock: + dependency: transitive + description: + name: clock + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + collection: + dependency: transitive + description: + name: collection + url: "https://pub.dartlang.org" + source: hosted + version: "1.15.0" + connectivity: + dependency: "direct main" + description: + name: connectivity + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.6" + connectivity_for_web: + dependency: transitive + description: + name: connectivity_for_web + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.0+1" + connectivity_macos: + dependency: transitive + description: + name: connectivity_macos + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.1+2" + connectivity_platform_interface: + dependency: transitive + description: + name: connectivity_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + cross_file: + dependency: transitive + description: + name: cross_file + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.3+1" + crypto: + dependency: transitive + description: + name: crypto + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.2" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.5" + device_info: + dependency: "direct main" + description: + name: device_info + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.3" + device_info_platform_interface: + dependency: transitive + description: + name: device_info_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + dio: + dependency: "direct main" + description: + name: dio + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.0" + equatable: + dependency: transitive + description: + name: equatable + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.3" + event_bus: + dependency: "direct main" + description: + name: event_bus + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + fake_async: + dependency: transitive + description: + name: fake_async + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + ffi: + dependency: transitive + description: + name: ffi + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.1" + file: + dependency: transitive + description: + name: file + url: "https://pub.dartlang.org" + source: hosted + version: "6.1.2" + file_picker: + dependency: "direct main" + description: + name: file_picker + url: "https://pub.dartlang.org" + source: hosted + version: "4.4.0" + fl_chart: + dependency: "direct main" + description: + name: fl_chart + url: "https://pub.dartlang.org" + source: hosted + version: "0.51.0" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_alarm_clock: + dependency: "direct main" + description: + name: flutter_alarm_clock + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.2" + flutter_blurhash: + dependency: transitive + description: + name: flutter_blurhash + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.4" + flutter_cache_manager: + dependency: "direct main" + description: + name: flutter_cache_manager + url: "https://pub.dartlang.org" + source: hosted + version: "3.3.0" + flutter_easyloading: + dependency: "direct main" + description: + name: flutter_easyloading + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.5" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.4" + flutter_localizations: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.6" + flutter_spinkit: + dependency: transitive + description: + name: flutter_spinkit + url: "https://pub.dartlang.org" + source: hosted + version: "5.1.0" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + fluttertoast: + dependency: "direct main" + description: + name: fluttertoast + url: "https://pub.dartlang.org" + source: hosted + version: "8.0.9" + http: + dependency: transitive + description: + name: http + url: "https://pub.dartlang.org" + source: hosted + version: "0.13.4" + http_parser: + dependency: transitive + description: + name: http_parser + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.1" + image_cropper: + dependency: "direct main" + description: + name: image_cropper + url: "https://pub.dartlang.org" + source: hosted + version: "1.5.1" + image_picker: + dependency: "direct main" + description: + name: image_picker + url: "https://pub.dartlang.org" + source: hosted + version: "0.8.5+3" + image_picker_android: + dependency: transitive + description: + name: image_picker_android + url: "https://pub.dartlang.org" + source: hosted + version: "0.8.4+13" + image_picker_for_web: + dependency: transitive + description: + name: image_picker_for_web + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.8" + image_picker_ios: + dependency: transitive + description: + name: image_picker_ios + url: "https://pub.dartlang.org" + source: hosted + version: "0.8.4+11" + image_picker_platform_interface: + dependency: transitive + description: + name: image_picker_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.5.0" + intl: + dependency: transitive + description: + name: intl + url: "https://pub.dartlang.org" + source: hosted + version: "0.17.0" + js: + dependency: transitive + description: + name: js + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.3" + lints: + dependency: transitive + description: + name: lints + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + matcher: + dependency: transitive + description: + name: matcher + url: "https://pub.dartlang.org" + source: hosted + version: "0.12.10" + meta: + dependency: transitive + description: + name: meta + url: "https://pub.dartlang.org" + source: hosted + version: "1.7.0" + mime: + dependency: transitive + description: + name: mime + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" + octo_image: + dependency: transitive + description: + name: octo_image + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" + open_file: + dependency: "direct main" + description: + name: open_file + url: "https://pub.dartlang.org" + source: hosted + version: "3.2.1" + path: + dependency: transitive + description: + name: path + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.0" + path_provider: + dependency: "direct main" + description: + name: path_provider + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.10" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.11" + path_provider_ios: + dependency: transitive + description: + name: path_provider_ios + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.7" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.6" + path_provider_macos: + dependency: transitive + description: + name: path_provider_macos + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.6" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.4" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.6" + pedantic: + dependency: transitive + description: + name: pedantic + url: "https://pub.dartlang.org" + source: hosted + version: "1.11.1" + permission_handler: + dependency: "direct main" + description: + name: permission_handler + url: "https://pub.dartlang.org" + source: hosted + version: "9.0.2" + permission_handler_platform_interface: + dependency: transitive + description: + name: permission_handler_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "3.7.0" + photo_view: + dependency: "direct main" + description: + name: photo_view + url: "https://pub.dartlang.org" + source: hosted + version: "0.11.1" + platform: + dependency: transitive + description: + name: platform + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.0" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.2" + process: + dependency: transitive + description: + name: process + url: "https://pub.dartlang.org" + source: hosted + version: "4.2.4" + rxdart: + dependency: transitive + description: + name: rxdart + url: "https://pub.dartlang.org" + source: hosted + version: "0.27.4" + share: + dependency: "direct main" + description: + name: share + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.4" + shared_preferences: + dependency: "direct main" + description: + name: shared_preferences + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.15" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.10" + shared_preferences_ios: + dependency: transitive + description: + name: shared_preferences_ios + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.9" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + shared_preferences_macos: + dependency: transitive + description: + name: shared_preferences_macos + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.4" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.4" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + sn_progress_dialog: + dependency: "direct main" + description: + name: sn_progress_dialog + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.8" + source_span: + dependency: transitive + description: + name: source_span + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.1" + sqflite: + dependency: transitive + description: + name: sqflite + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.2" + sqflite_common: + dependency: transitive + description: + name: sqflite_common + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1+1" + stack_trace: + dependency: transitive + description: + name: stack_trace + url: "https://pub.dartlang.org" + source: hosted + version: "1.10.0" + stream_channel: + dependency: transitive + description: + name: stream_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + string_scanner: + dependency: transitive + description: + name: string_scanner + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + synchronized: + dependency: transitive + description: + name: synchronized + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0+2" + term_glyph: + dependency: transitive + description: + name: term_glyph + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + test_api: + dependency: transitive + description: + name: test_api + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.2" + timelines: + dependency: "direct main" + description: + name: timelines + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.0" + typed_data: + dependency: transitive + description: + name: typed_data + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.0" + url_launcher: + dependency: "direct main" + description: + name: url_launcher + url: "https://pub.dartlang.org" + source: hosted + version: "6.1.2" + url_launcher_android: + dependency: transitive + description: + name: url_launcher_android + url: "https://pub.dartlang.org" + source: hosted + version: "6.0.14" + url_launcher_ios: + dependency: transitive + description: + name: url_launcher_ios + url: "https://pub.dartlang.org" + source: hosted + version: "6.0.14" + url_launcher_linux: + dependency: transitive + description: + name: url_launcher_linux + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" + url_launcher_macos: + dependency: transitive + description: + name: url_launcher_macos + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" + url_launcher_platform_interface: + dependency: transitive + description: + name: url_launcher_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.5" + url_launcher_web: + dependency: transitive + description: + name: url_launcher_web + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.7" + url_launcher_windows: + dependency: transitive + description: + name: url_launcher_windows + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" + uuid: + dependency: transitive + description: + name: uuid + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.6" + vector_math: + dependency: transitive + description: + name: vector_math + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + win32: + dependency: transitive + description: + name: win32 + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.6" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.0+1" +sdks: + dart: ">=2.14.0 <3.0.0" + flutter: ">=2.5.0" diff --git a/study-app-tutorials/pubspec.yaml b/study-app-tutorials/pubspec.yaml new file mode 100644 index 000000000..2eeae75c4 --- /dev/null +++ b/study-app-tutorials/pubspec.yaml @@ -0,0 +1,222 @@ +name: tutorials +description: A tutorials project. + +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +# The following defines the version and build number for your application. +# A version number is three numbers separated by dots, like 1.2.43 +# followed by an optional build number separated by a +. +# Both the version and the builder number may be overridden in flutter +# build by specifying --build-name and --build-number, respectively. +# In Android, build-name is used as versionName while build-number used as versionCode. +# Read more about Android versioning at https://developer.android.com/studio/publish/versioning +# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. +# Read more about iOS versioning at +# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html +version: 1.0.0+1 + +environment: + sdk: ">=2.12.0 <3.0.0" + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.2 + + + +################################################################################################ + ################ 3 third start added by bage ################ + flutter_localizations: + sdk: flutter + + + # https://github.com/LaoMengFlutter/flutter-do/tree/master/flutter_app_upgrade + # flutter_app_upgrade: ^1.1.0 + + # https://github.com/flutterchina/dio + # https://pub.flutter-io.cn/packages/dio + dio: 4.0.0 + + # https://pub.flutter-io.cn/packages/permission_handler/install + permission_handler: ^9.0.0 + + # https://pub.flutter-io.cn/packages/path_provider + path_provider: ^2.0.2 + + # https://pub.flutter-io.cn/packages/file_picker + file_picker: ^4.3.3 + + # https://pub.flutter-io.cn/packages/install_plugin + # install_plugin: ^2.0.1 + + # https://pub.flutter-io.cn/packages/open_file + open_file: ^3.0.3 + + # https://pub.flutter-io.cn/packages/fluttertoast/install + fluttertoast: ^8.0.9 + + # https://pub.dev/packages/shared_preferences + shared_preferences: ^2.0.6 + + # https://pub.flutter-io.cn/packages/url_launcher/install + url_launcher: ^6.1.2 + + # https://pub.dev/packages/share/install + share: ^2.0.4 + + # https://pub.flutter-io.cn/packages/connectivity + connectivity: ^3.0.6 + + # https://pub.dev/packages/device_info + device_info: ^2.0.2 + + # https://pub.dev/packages/photo_view + photo_view: ^0.11.1 + + # https://pub.dev/packages/cached_network_image/install + cached_network_image: ^3.0.0 + + # https://pub.dev/packages/timelines + timelines: ^0.1.0 + + # https://pub.flutter-io.cn/packages/fl_chart/install + fl_chart: ^0.51.0 + + # https://pub.flutter-io.cn/packages/flutter_cache_manager/example + flutter_cache_manager: ^3.3.0 + + # https://pub.flutter-io.cn/packages/sn_progress_dialog + sn_progress_dialog: ^1.0.8 + + + # https://pub.dev/packages/fl_chart + # fl_chart: ^0.36.2 + + # https://pub.flutter-io.cn/packages/flutter_staggered_grid_view/install + # flutter_staggered_grid_view: ^0.4.0 + + # https://pub.dev/packages/zefyr/install + # zefyr: ^0.12.0 + + # https://pub.dev/packages/extended_text_field/install + # extended_text_field: ^8.0.0 + + # https://pub.dev/packages/event_bus + event_bus: ^2.0.0 + + # https://pub.flutter-io.cn/packages/image_picker/example + image_picker: ^0.8.5+3 + + # https://pub.flutter-io.cn/packages/image_cropper + image_cropper: ^1.5.0 + + # https://pub.flutter-io.cn/packages/flutter_easyloading + flutter_easyloading: ^3.0.5 + + # https://pub.flutter-io.cn/packages/flutter_alarm_clock/install + flutter_alarm_clock: ^0.0.2 + + + # https://pub.flutter-io.cn/packages/google_fonts +# google_fonts: ^2.1.1 + + + # # https://pub.dev/packages/timeago + # timeago: ^3.1.0 + + # https://pub.dev/packages/google_fonts + # https://pub.dev/packages/json_serializable + # https://pub.dev/packages/sentry + # https://pub.dev/packages/geolocator + # https://pub.dev/packages/share_plus + # https://pub.dev/packages/flutter_local_notifications + # https://pub.dev/packages/image_picker + # https://pub.dev/packages/cached_network_image + # https://pub.dev/packages/carousel_slider 图片侧滑 + # https://pub.dev/packages/introduction_screen + # https://pub.dev/packages/video_player + # https://pub.dev/packages/photo_view + # https://pub.dev/packages/timeago + # https://pub.dev/packages/directed_graph + # https://pub.dev/packages/dash_chat + # https://pub.dev/packages/stream_chat_flutter + # https://pub.dev/packages/flutter_linkify # 富文本显示 + # https://pub.dev/packages/extended_text_field + # https://pub.dev/packages/zefyr/install + # https://pub.dev/packages/dart_json_mapper + # https://pub.dev/packages/showcaseview + # https://pub.dev/packages/logger + + + +dev_dependencies: + flutter_test: + sdk: flutter + + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^1.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware. + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages + + assets: + - assets/locale/ + - assets/images/ + + diff --git a/study-app-tutorials/sign.jks b/study-app-tutorials/sign.jks new file mode 100644 index 000000000..10214693c Binary files /dev/null and b/study-app-tutorials/sign.jks differ diff --git a/study-arthas/pom.xml b/study-arthas/pom.xml index 4be57d3d8..9dd285a0d 100644 --- a/study-arthas/pom.xml +++ b/study-arthas/pom.xml @@ -15,9 +15,9 @@ http://www.example.com - UTF-8 - 1.7 - 1.7 + + 1.8 + 1.8 diff --git a/study-axon/pom.xml b/study-axon/pom.xml index 9e9fb12ab..d515be186 100644 --- a/study-axon/pom.xml +++ b/study-axon/pom.xml @@ -28,8 +28,10 @@ - UTF-8 + 2.0.1.RELEASE + 1.8 + 1.8 diff --git a/study-baidu-asr/pom.xml b/study-baidu-asr/pom.xml index 91eec1b40..8148c0532 100644 --- a/study-baidu-asr/pom.xml +++ b/study-baidu-asr/pom.xml @@ -16,9 +16,9 @@ http://www.example.com - UTF-8 - 1.7 - 1.7 + + 1.8 + 1.8 diff --git a/study-cache-memory-spring/pom.xml b/study-cache-memory-spring/pom.xml index 4c4ff630f..d2a5d1a1f 100644 --- a/study-cache-memory-spring/pom.xml +++ b/study-cache-memory-spring/pom.xml @@ -17,7 +17,7 @@ 1.8 1.8 - UTF-8 + 4.1.9.RELEASE UTF-8 3.0-alpha-1 diff --git a/study-cache-memory-springboot/pom.xml b/study-cache-memory-springboot/pom.xml index 8797be462..0bcf07fae 100644 --- a/study-cache-memory-springboot/pom.xml +++ b/study-cache-memory-springboot/pom.xml @@ -13,6 +13,8 @@ http://maven.apache.org UTF-8 + 1.8 + 1.8 2.0.1.RELEASE diff --git a/study-cas-client/pom.xml b/study-cas-client/pom.xml index 8cea223aa..767d2604b 100644 --- a/study-cas-client/pom.xml +++ b/study-cas-client/pom.xml @@ -14,7 +14,8 @@ 3.5.0 - + 1.8 + 1.8 UTF-8 4.3.9.RELEASE UTF-8 diff --git a/study-cas-server/pom.xml b/study-cas-server/pom.xml index 78baad5a2..010a91936 100644 --- a/study-cas-server/pom.xml +++ b/study-cas-server/pom.xml @@ -16,4 +16,12 @@ study-cas-server + + + UTF-8 + 1.8 + 1.8 + + + diff --git a/study-cglib/pom.xml b/study-cglib/pom.xml index cd0a8c220..245d62f24 100644 --- a/study-cglib/pom.xml +++ b/study-cglib/pom.xml @@ -12,6 +12,8 @@ http://maven.apache.org UTF-8 + 1.8 + 1.8 diff --git a/study-code-format/pom.xml b/study-code-format/pom.xml index 4a418a669..a459d0a1a 100644 --- a/study-code-format/pom.xml +++ b/study-code-format/pom.xml @@ -12,6 +12,8 @@ http://maven.apache.org UTF-8 + 1.8 + 1.8 diff --git a/study-crawler/README.md b/study-crawler/README.md new file mode 100644 index 000000000..f47a83111 --- /dev/null +++ b/study-crawler/README.md @@ -0,0 +1,25 @@ +# study-crawler # +抓取 Java + +## 参考链接 ## +https://github.com/code4craft/webmagic + +https://github.com/yasserg/crawler4j + + + + + + + + + + + + + + + + + + diff --git a/study-crawler/pom.xml b/study-crawler/pom.xml new file mode 100644 index 000000000..b54a72bff --- /dev/null +++ b/study-crawler/pom.xml @@ -0,0 +1,63 @@ + + + + 4.0.0 + + com.bage + study-crawler + 0.0.1-SNAPSHOT + + + + org.apache.maven.plugins + maven-compiler-plugin + + 8 + 8 + + + + + + study-crawler + http://www.example.com + + + UTF-8 + 1.7 + 1.7 + + + + + junit + junit + 4.11 + test + + + + us.codecraft + webmagic-core + 0.7.5 + + + us.codecraft + webmagic-extension + 0.7.5 + + + + + + edu.uci.ics + crawler4j + 4.4.0 + + + + + + + diff --git a/study-crawler/src/main/java/com/bage/crawler4j/Controller.java b/study-crawler/src/main/java/com/bage/crawler4j/Controller.java new file mode 100644 index 000000000..89b8bbacc --- /dev/null +++ b/study-crawler/src/main/java/com/bage/crawler4j/Controller.java @@ -0,0 +1,37 @@ +package com.bage.crawler4j; + +import edu.uci.ics.crawler4j.crawler.CrawlConfig; +import edu.uci.ics.crawler4j.crawler.CrawlController; +import edu.uci.ics.crawler4j.fetcher.PageFetcher; +import edu.uci.ics.crawler4j.robotstxt.RobotstxtConfig; +import edu.uci.ics.crawler4j.robotstxt.RobotstxtServer; + +public class Controller { + public static void main(String[] args) throws Exception { + String crawlStorageFolder = "/data/crawl/root"; + int numberOfCrawlers = 7; + + CrawlConfig config = new CrawlConfig(); + config.setCrawlStorageFolder(crawlStorageFolder); + + // Instantiate the controller for this crawl. + PageFetcher pageFetcher = new PageFetcher(config); + RobotstxtConfig robotstxtConfig = new RobotstxtConfig(); + RobotstxtServer robotstxtServer = new RobotstxtServer(robotstxtConfig, pageFetcher); + CrawlController controller = new CrawlController(config, pageFetcher, robotstxtServer); + + // For each crawl, you need to add some seed urls. These are the first + // URLs that are fetched and then the crawler starts following links + // which are found in these pages + controller.addSeed("https://www.ics.uci.edu/~lopes/"); + controller.addSeed("https://www.ics.uci.edu/~welling/"); + controller.addSeed("https://www.ics.uci.edu/"); + +// // The factory which creates instances of crawlers. +// CrawlController.WebCrawlerFactory factory = MyCrawler::new; +// +// // Start the crawl. This is a blocking operation, meaning that your code +// // will reach the line after this only when crawling is finished. +// controller.start(factory, numberOfCrawlers); + } +} \ No newline at end of file diff --git a/study-crawler/src/main/java/com/bage/crawler4j/MyCrawler.java b/study-crawler/src/main/java/com/bage/crawler4j/MyCrawler.java new file mode 100644 index 000000000..e85a34b04 --- /dev/null +++ b/study-crawler/src/main/java/com/bage/crawler4j/MyCrawler.java @@ -0,0 +1,53 @@ +package com.bage.crawler4j; + +import edu.uci.ics.crawler4j.crawler.Page; +import edu.uci.ics.crawler4j.crawler.WebCrawler; +import edu.uci.ics.crawler4j.parser.HtmlParseData; +import edu.uci.ics.crawler4j.url.WebURL; + +import java.util.Set; +import java.util.regex.Pattern; + +public class MyCrawler extends WebCrawler { + + private final static Pattern FILTERS = Pattern.compile(".*(\\.(css|js|gif|jpg" + + "|png|mp3|mp4|zip|gz))$"); + + /** + * This method receives two parameters. The first parameter is the page + * in which we have discovered this new url and the second parameter is + * the new url. You should implement this function to specify whether + * the given url should be crawled or not (based on your crawling logic). + * In this example, we are instructing the crawler to ignore urls that + * have css, js, git, ... extensions and to only accept urls that start + * with "https://www.ics.uci.edu/". In this case, we didn't need the + * referringPage parameter to make the decision. + */ + @Override + public boolean shouldVisit(Page referringPage, WebURL url) { + String href = url.getURL().toLowerCase(); + return !FILTERS.matcher(href).matches() + && href.startsWith("https://www.ics.uci.edu/"); + } + + /** + * This function is called when a page is fetched and ready + * to be processed by your program. + */ + @Override + public void visit(Page page) { + String url = page.getWebURL().getURL(); + System.out.println("URL: " + url); + + if (page.getParseData() instanceof HtmlParseData) { + HtmlParseData htmlParseData = (HtmlParseData) page.getParseData(); + String text = htmlParseData.getText(); + String html = htmlParseData.getHtml(); + Set links = htmlParseData.getOutgoingUrls(); + + System.out.println("Text length: " + text.length()); + System.out.println("Html length: " + html.length()); + System.out.println("Number of outgoing links: " + links.size()); + } + } +} \ No newline at end of file diff --git a/study-crawler/src/main/java/com/bage/webmagic/GithubRepoPageProcessor.java b/study-crawler/src/main/java/com/bage/webmagic/GithubRepoPageProcessor.java new file mode 100644 index 000000000..a9ce451c9 --- /dev/null +++ b/study-crawler/src/main/java/com/bage/webmagic/GithubRepoPageProcessor.java @@ -0,0 +1,32 @@ +package com.bage.webmagic; + +import us.codecraft.webmagic.Page; +import us.codecraft.webmagic.Site; +import us.codecraft.webmagic.Spider; +import us.codecraft.webmagic.processor.PageProcessor; + +public class GithubRepoPageProcessor implements PageProcessor { + + private Site site = Site.me().setRetryTimes(3).setSleepTime(1000); + + @Override + public void process(Page page) { + page.addTargetRequests(page.getHtml().links().regex("(https://github\\.com/\\w+/\\w+)").all()); + page.putField("author", page.getUrl().regex("https://github\\.com/(\\w+)/.*").toString()); + page.putField("name", page.getHtml().xpath("//h1[@class='public']/strong/a/text()").toString()); + if (page.getResultItems().get("name")==null){ + //skip this page + page.setSkip(true); + } + page.putField("readme", page.getHtml().xpath("//div[@id='readme']/tidyText()")); + } + + @Override + public Site getSite() { + return site; + } + + public static void main(String[] args) { + Spider.create(new GithubRepoPageProcessor()).addUrl("https://github.com/code4craft").thread(5).run(); + } +} \ No newline at end of file diff --git a/study-crawler/src/test/java/com/bage/HHHH.java b/study-crawler/src/test/java/com/bage/HHHH.java new file mode 100644 index 000000000..a2ec144c5 --- /dev/null +++ b/study-crawler/src/test/java/com/bage/HHHH.java @@ -0,0 +1,13 @@ +package com.bage; + +import org.junit.Test; + +public class HHHH { + + @Test + public void hello() { + System.out.println("JIA/XIAOJIN".split("/")[0]); +// orderInfoSyncProcessor.process(); + } + +} diff --git a/study-cryption/pom.xml b/study-cryption/pom.xml index 1e5a3d6b4..68017bc2c 100644 --- a/study-cryption/pom.xml +++ b/study-cryption/pom.xml @@ -12,6 +12,8 @@ http://maven.apache.org UTF-8 + 1.8 + 1.8 diff --git a/study-cxf/pom.xml b/study-cxf/pom.xml index 6f6684dcf..179a882bf 100644 --- a/study-cxf/pom.xml +++ b/study-cxf/pom.xml @@ -19,6 +19,8 @@ 3.1.9 1.9.13 4.1.9.RELEASE + 1.8 + 1.8 diff --git a/study-dbschema-to-json/pom.xml b/study-dbschema-to-json/pom.xml index b4c56e0d5..2b8432bf4 100644 --- a/study-dbschema-to-json/pom.xml +++ b/study-dbschema-to-json/pom.xml @@ -20,6 +20,8 @@ UTF-8 2.0.1.RELEASE springio + 1.8 + 1.8 diff --git a/study-docker/README.md b/study-docker/README.md index 8eba4506b..f9957cc90 100644 --- a/study-docker/README.md +++ b/study-docker/README.md @@ -41,6 +41,30 @@ Most users set up Docker’s repositories and install from them, for ease of ins 配置开机启动 > systemctl enable docker + + +docker hub 设置国内镜像地址 + +https://www.csdn.net/tags/NtzaMgzsNDA4Ni1ibG9n.html + +在任务栏点击 Docker Desktop 应用图标 -> Perferences,在左侧导航菜单选择 Docker Engine,在右侧像下边一样编辑 json 文件。修改完成之后,点击 Apply & Restart 按钮,Docker 就会重启并应用配置的镜像地址了。 + +```json +{ + "registry-mirrors": [ + "https://9cpn8tt6.mirror.aliyuncs.com", + "https://hub-mirror.c.163.com", + "https://registry.docker-cn.com" + ] +} +``` + +验证 + +``` +docker info +``` + ## 常用命令 ## ### Docker ### @@ -135,9 +159,37 @@ Docker Pull Command docker pull mysql +Mac M1 报错 + +``` +docker pull mysql + +Using default tag: latest + +latest: Pulling from library/mysql + +no matching manifest for linux/arm64/v8 in the manifest list entries + +``` + +解决方案 + +``` +docker pull mysql/mysql-server +``` + + + Start a mysql server instance docker run --name bage-mysql -v /home/bage/data/mysql:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=bage -p 3306:3306 -d bage-mysql + + Mac: + docker run --name bage-mysql -v /Users/bage/bage/docker-data/mysql:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=bage -p 3306:3306 -d mysql/mysql-server + + +​ +​ 其中 --name 起名 bage-mysql @@ -369,10 +421,19 @@ Docker Pull Command Docker Pull Command docker pull elasticsearch:7.5.1 + + Mac M1 + docker pull elasticsearch:7.16.2 + + +​ 启动 docker run --network myapp --name elasticsearch -p 9092:9200 -p 8093:9300 -e "discovery.type=single-node" elasticsearch:7.5.1 + + Mac + docker run --network myapp --name elasticsearch -p 9092:9200 -p 8093:9300 -e "discovery.type=single-node" elasticsearch:7.11.1 @@ -380,10 +441,12 @@ Docker Pull Command - 访问 http://{ip}:9092/_cat/health + + http://127.0.0.1:9092/_cat/health + http://127.0.0.1:8093/_cat/health ### 安装部署 zipkin ### @@ -682,6 +745,13 @@ start a instance + + + + + + + ### 安装配置 logstash ### 版本匹配 https://www.elastic.co/cn/support/matrix#matrix_compatibility 参考链接:[https://www.elastic.co/guide/en/logstash/current/docker.html](https://www.elastic.co/guide/en/logstash/current/docker.html)、[https://hub.docker.com/_/logstash?tab=description](https://hub.docker.com/_/logstash?tab=description)、[https://www.elastic.co/guide/en/logstash/current/docker-config.html](https://www.elastic.co/guide/en/logstash/current/docker-config.html) @@ -689,6 +759,9 @@ start a instance Docker Pull Command docker pull logstash:7.5.1 + + Mac M1 + docker pull logstash:7.16.2 start a instance[not enough space] @@ -709,28 +782,39 @@ setting max_map_count sudo sysctl -w vm.max_map_count=262144 vi /home/bage/data/logstash/file-beats.conf - - # 数据输入配置:port -> 端口号;codec -> 输入格式。这里以logback为例。 - input { - tcp { - port => 5044 - codec=>json_lines - } - } - - # 数据输出配置:hosts -> 主机集合;index -> 你将要创建的索引名称。这里es为例。 - output { - elasticsearch { - hosts => ["127.0.0.1:9200"] - index => "%{[appName]}-%{+YYYY.MM.dd}" - } - } + +[Mac: /Users/bage/bage/docker-data/elk/logstash/beats-input.conf] + +``` +# 数据输入配置:port -> 端口号;codec -> 输入格式。这里以logback为例。 +input { + tcp { + port => 5044 + codec=>json_lines + } +} + +# 数据输出配置:hosts -> 主机集合;index -> 你将要创建的索引名称。这里es为例。 +output { + elasticsearch { + hosts => ["127.0.0.1:9200"] + index => "%{[appName]}-%{+YYYY.MM.dd}" + } +} +``` + + start a instance docker run -v /home/bage/data/logstash/file-beats.conf:/etc/logstash/conf.d/02-beats-input.conf -p 8056:5601 -p 8092:9200 -p 8044:5044 -it --name elk sebp/elk:700 - + + Mac: + docker run -v /Users/bage/bage/docker-data/elk/logstash/beats-input.conf:/etc/logstash/conf.d/02-beats-input.conf -p 8056:5601 -p 8092:9200 -p 8044:5044 -it --name elk sebp/elk:700 + + Mac[多目录挂载问题]: + docker run -e MAX_MAP_COUNT="262144" -v /Users/bage/bage/docker-data/elk/es:/var/lib/elasticsearch -v /Users/bage/bage/docker-data/elk/logstash/beats-input.conf:/etc/logstash/conf.d/02-beats-input.conf -p 8056:5601 -p 8092:9200 -p 8044:5044 -it --name elk sebp/elk:700 访问 @@ -752,6 +836,10 @@ kibana Docker Pull Command docker pull kibana:7.5.1 + + Mac + docker pull kibana:7.16.2 + start a instance @@ -938,6 +1026,321 @@ start a instance +### 安装配置 Nacos + +参考链接: https://hub.docker.com/r/nacos/nacos-server + +Docker Pull Command + +``` +docker pull nacos/nacos-server +``` + +start a instance + +``` +docker run --name nacos-quick -e MODE=standalone -p 8849:8848 -d nacos/nacos-server:2.0.2 +``` + +Docker Pull Command + +``` +http:localhost:8849 +``` + + + + + +### docker 安装ELK 【Mac】 + +#### 设置国内镜像 + +参考链接: https://www.csdn.net/tags/NtzaMgzsNDA4Ni1ibG9n.html + +**操作步骤** + +Docker Desktop 应用图标 -> Perferences,在左侧导航菜单选择 Docker Engine,在右侧像下边一样编辑 json 文件。修改完成之后,点击 Apply & Restart 按钮,Docker 就会重启并应用配置的镜像地址了。 + +```json +{ + "registry-mirrors": [ + "https://9cpn8tt6.mirror.aliyuncs.com", + "https://hub-mirror.c.163.com", + "https://registry.docker-cn.com" + ] +} +``` + +验证 + +``` +docker info +``` + + + +### 安装ES【Mac】 + +Docker Pull Command + +``` +docker pull elasticsearch:7.16.2 + +``` + +创建基础目录 + +``` +mkdir /Users/bage/bage/docker-data/es + +bage % pwd +/Users/bage/bage/docker-data/es + +bage % ls +config data nodes plugins +``` + +编辑文件 + +``` +vi /Users/bage/bage/docker-data/es/config/elasticearch.yml + + +``` + +启动 + +``` +docker run --network myapp --name bage-es -p 9092:9200 -p 8093:9300 \ +-e "discovery.type=single-node" \ +-v /Users/bage/bage/docker-data/es/config/elasticearch.yml:/usr/share/elasticsearch/config/elasticearch.yml \ +-v /Users/bage/bage/docker-data/es/data:/usr/share/elasticsearch/data \ +-v /Users/bage/bage/docker-data/es/plugins:/usr/share/elasticsearch/plugins \ +-d elasticsearch:7.16.2 + +``` + + 访问 + +``` +http://127.0.0.1:9092/_cat/health +http://127.0.0.1:9092 + +查看索引 +http://localhost:9092/_cat/indices?v&pretty +``` + + + +### 安装kibana【Mac】 + +Docker Pull Command + +``` +docker pull kibana:7.16.2 + +``` + +创建基础目录 + +``` +mkdir /Users/bage/bage/docker-data/kibana + +bage % pwd +/Users/bage/bage/docker-data/kibana + +bage % ls +config +``` + +编辑文件[暂时不使用] + +``` +/Users/bage/bage/docker-data/kibana/config/kibana.yml + +server.port: 8056 +server.host: "0.0.0.0" +elasticsearch.url: "http://bage-es:9092" + +``` + +start a instance + +``` +docker run --network myapp -d --link bage-es:elasticsearch -p 9056:5601 --name bage-kibana \ +-d kibana:7.16.2 + +``` + +visit + +``` +http://127.0.0.1:9056/app/kibana + +http://127.0.0.1:9056/app/kibana#/dev_tools/console +``` + +**kibana里建立索引** + +通过kiban菜单去建立索引:Management>Index patterns>Create index pattern,这里会显示可用的索引名称。 + +可以直接搜索 index 找到 Create index pattern + + + +选择对应的索引 + + + +Discover 里面创建 看板 + +### 安装 logstash【Mac】 + +参考链接 https://www.elastic.co/guide/en/logstash/7.17/docker.html + +https://github.com/elastic/logstash/blob/7.17/config/logstash.yml + +Docker Pull Command + +``` +docker pull logstash:7.16.2 +``` + +创建基础目录 + +``` +mkdir /Users/bage/bage/docker-data/logstash + +bage % pwd +/Users/bage/bage/docker-data/logstash + +bage % ls +pipeline config + +``` + +编辑文件 + +``` +vi /Users/bage/bage/docker-data/logstash/config/logstash.yml + +http.host: "0.0.0.0" +# xpack.monitoring.elasticsearch.hosts: ["http://bage-es:9092"] +monitoring.elasticsearch.hosts: ["http://bage-es:9092"] +# path.config: /usr/share/logstash/config/conf.d/*.conf +# path.logs: /usr/share/logstash/logs + +``` + + + +``` +vi /Users/bage/bage/docker-data/logstash/pipeline/logstash.conf + +# Sample Logstash configuration for creating a simple +# Beats -> Logstash -> Elasticsearch pipeline. + +input { + tcp { + mode => "server" + port => 5044 + codec => json_lines + stat_interval => "5" + } +} + +output { + elasticsearch { + hosts => ["http://bage-es:9200"] + index => "{{indexName}}" + #user => "elastic" + #password => "changeme" + } +} + +``` + +start a instance + +``` +docker run --network myapp --name=bage-logstash \ +-p 8056:5601 \ +-p 8044:5044 \ +-v /Users/bage/bage/docker-data/logstash/config/logstash.yml:/usr/share/logstash/config/logstash.yml \ +-v /Users/bage/bage/docker-data/logstash/pipeline/:/usr/share/logstash/pipeline/ \ +-d logstash:7.16.2 + + +docker run --name bage-logstash --network myapp -p 8056:5601 --link bage-es:elasticsearch -d logstash:7.16.2 + +``` + + + +### 安装 OceanBase【Mac】 + +参考链接 + +https://open.oceanbase.com/docs/observer-cn/V3.1.3/10000000000096600 + +https://hub.docker.com/r/oceanbase/oceanbase-ce + +Docker Pull Command + +``` +docker pull :7.16.2 +``` + +To start an OceanBase instance, run this command: + +```bash +docker run -p 2881:2881 --name obstandalone -d oceanbase/oceanbase-ce +``` + + + +### 安装 Jenkins【Mac,toCheck】 + +参考链接 + +https://www.jenkins.io/zh/doc/book/installing/#downloading-and-running-jenkins-in-docker + +https://hub.docker.com/r/jenkinsci/blueocean/ + +Docker Pull Command + +``` +docker pull jenkinsci/blueocean +``` + +Run + +``` +docker run --name=bage-jenkins -p 8080:8080 jenkinsci/blueocean + +docker run --name=bage-jenkins -u root -p 8080:8080 -p 50000:50000 -v /var/run/docker.sock:/var/run/docker.sock jenkinsci/blueocean + +``` + +To start an Jenkins instance, run this command: + +```bash +docker run \ + -u root \ + --rm \ + -d \ + -p 8080:8080 \ + -p 50000:50000 \ + -v jenkins-data:/var/jenkins_home \ + -v /var/run/docker.sock:/var/run/docker.sock \ + jenkinsci/blueocean + + + +``` + + diff --git a/study-docker/pom.xml b/study-docker/pom.xml index 5da7b633f..80de25688 100644 --- a/study-docker/pom.xml +++ b/study-docker/pom.xml @@ -12,6 +12,8 @@ http://maven.apache.org UTF-8 + 1.8 + 1.8 diff --git a/study-domain-generator/pom.xml b/study-domain-generator/pom.xml index c8ddc6f4b..be75927c2 100644 --- a/study-domain-generator/pom.xml +++ b/study-domain-generator/pom.xml @@ -17,8 +17,8 @@ UTF-8 - 1.7 - 1.7 + 1.8 + 1.8 diff --git a/study-downtime/pom.xml b/study-downtime/pom.xml index 767c63199..efed404c9 100644 --- a/study-downtime/pom.xml +++ b/study-downtime/pom.xml @@ -17,8 +17,8 @@ UTF-8 - 1.7 - 1.7 + 1.8 + 1.8 diff --git a/study-draw-io/Untitled Diagram.drawio b/study-draw-io/Untitled Diagram.drawio new file mode 100644 index 000000000..4aac42e27 --- /dev/null +++ b/study-draw-io/Untitled Diagram.drawio @@ -0,0 +1,175 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/study-draw-io/readme.MD b/study-draw-io/readme.MD new file mode 100644 index 000000000..30d82be33 --- /dev/null +++ b/study-draw-io/readme.MD @@ -0,0 +1,3 @@ + +Draw IO ļ + diff --git a/study-draw-io/tis-call-trace.drawio b/study-draw-io/tis-call-trace.drawio new file mode 100644 index 000000000..f2f89c3bb --- /dev/null +++ b/study-draw-io/tis-call-trace.drawio @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/study-dubbo/pom.xml b/study-dubbo/pom.xml index dc3c034fd..1a9fae841 100644 --- a/study-dubbo/pom.xml +++ b/study-dubbo/pom.xml @@ -12,6 +12,8 @@ http://maven.apache.org UTF-8 + 1.8 + 1.8 @@ -30,7 +32,7 @@ org.apache.zookeeper zookeeper - 3.3.3 + 3.4.13 diff --git a/study-easyexcel/README.md b/study-easyexcel/README.md index 2f75171af..22c5be0d1 100644 --- a/study-easyexcel/README.md +++ b/study-easyexcel/README.md @@ -2,9 +2,8 @@ easyexcel 学习笔记 ## 参考地址 ## -参考地址 [https://github.com/alibaba/easyexcel](https://github.com/alibaba/easyexcel) -快速开始 [https://github.com/alibaba/easyexcel/blob/master/quickstart.md](https://github.com/alibaba/easyexcel/blob/master/quickstart.md) - +https://github.com/alibaba/easyexcel +https://easyexcel.opensource.alibaba.com/docs/current/ diff --git a/study-easyexcel/pom.xml b/study-easyexcel/pom.xml index 8ce90bfcb..6ac3584f3 100644 --- a/study-easyexcel/pom.xml +++ b/study-easyexcel/pom.xml @@ -17,8 +17,8 @@ UTF-8 - 1.7 - 1.7 + 1.8 + 1.8 diff --git a/study-ehcache/pom.xml b/study-ehcache/pom.xml index 07fdaac42..c3496a5e9 100644 --- a/study-ehcache/pom.xml +++ b/study-ehcache/pom.xml @@ -12,6 +12,14 @@ war study-ehcache Maven Webapp http://maven.apache.org + + + + UTF-8 + 1.8 + 1.8 + + junit diff --git a/study-es/README.md b/study-es/README.md index a950377c9..5d79c65cc 100644 --- a/study-es/README.md +++ b/study-es/README.md @@ -5,4 +5,9 @@ ES 学习笔记 ## 参考文档 ## 官网 [https://www.elastic.co/](https://www.elastic.co/) Docker 安装 ES [https://github.com/bage2014/study/tree/master/study-docker](https://github.com/bage2014/study/tree/master/study-docker) -Spring 官网 [https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/](https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/) \ No newline at end of file +Spring 官网 [https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/](https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/) + +https://www.cnblogs.com/sunsky303/p/9438737.html +https://baijiahao.baidu.com/s?id=1709437902597420314&wfr=spider&for=pc + + diff --git a/study-es/pom.xml b/study-es/pom.xml index 27263ec1f..7598cf0ea 100644 --- a/study-es/pom.xml +++ b/study-es/pom.xml @@ -17,8 +17,8 @@ UTF-8 - 1.7 - 1.7 + 1.8 + 1.8 diff --git a/study-ews-java-api/pom.xml b/study-ews-java-api/pom.xml index 581a4e24c..d95793383 100644 --- a/study-ews-java-api/pom.xml +++ b/study-ews-java-api/pom.xml @@ -13,8 +13,8 @@ UTF-8 - 1.7 - 1.7 + 1.8 + 1.8 diff --git a/study-file-online-preview/README.md b/study-file-online-preview/README.md new file mode 100644 index 000000000..b4d1a82e8 --- /dev/null +++ b/study-file-online-preview/README.md @@ -0,0 +1,2 @@ +https://gitee.com/kekingcn/file-online-preview/blob/master/README.md + diff --git a/study-file-online-preview/pom.xml b/study-file-online-preview/pom.xml new file mode 100644 index 000000000..1243963f1 --- /dev/null +++ b/study-file-online-preview/pom.xml @@ -0,0 +1,23 @@ + + + + study + com.bage + 0.0.1-SNAPSHOT + + 4.0.0 + + study-file-online-preview + + + 8 + 8 + + + + + + + \ No newline at end of file diff --git a/study-flutter/README.md b/study-flutter/README.md index 4a3739342..171ff987b 100644 --- a/study-flutter/README.md +++ b/study-flutter/README.md @@ -6,7 +6,8 @@ D:\softwares\SDK\emulator\emulator -avd Pixel2XLAPI29 -dns-server 10.0.3.2 flutter build apk --no-sound-null-safety ``` - +盛情域名??? +服务器 ?? ## Flutter 入门 @@ -17,18 +18,16 @@ https://developer.android.google.cn/studio/run/managing-avds#createavd https://fluttergems.dev/ -### TODO - -发布线上 +https://github.com/jahnli/awesome-flutter-plugins -用户评价 +### TODO -版本信息 +服务端代码改造 网络提示,请先连接网络 同意提示,多语言问题 -下载进度条(参考https://github.com/rhymelph/r_upgrade/blob/v0.3.2/README_CN.md) +下载进度条(参考 https://github.com/rhymelph/r_upgrade/blob/v0.3.2/README_CN.md) 分享 @@ -38,6 +37,29 @@ https://fluttergems.dev/ 大厂内推APP +https://github.com/lohanidamodar/flutter_ui_challenges + + + +https://github.com/FlutterOpen/flutter-ui-nice + + + + + +小组活动:每周体验一款app,产品包括最近热门,评价高,国外好的产品。建议从产品、市场两方面发表看法,也可以自由交流~ +产品方面: +1.主要功能 +2.解决用户什么需求 +3.独特卖点 +4.关键指标 + +市场方面: +1.用户群 +2.推广渠道 +3.收入分析 +4.成本分析 + app idea reference: @@ -71,6 +93,8 @@ https://github.com/mitesh77/Best-Flutter-UI-Templates https://github.com/iampawan/Flutter-UI-Kit +https://github.com/olayemii/flutter-ui-kits + https://github.com/flutter/gallery/tree/master/lib diff --git a/study-flutter/android/build.gradle b/study-flutter/android/build.gradle index a0c25a856..d93dc0d15 100644 --- a/study-flutter/android/build.gradle +++ b/study-flutter/android/build.gradle @@ -1,8 +1,8 @@ buildscript { repositories { maven { url 'https://dl.google.com/dl/android/maven2' } - google() - jcenter() +// google() +// jcenter() maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' } maven { url 'https://maven.aliyun.com/repository/gradle-plugin' } maven { url 'https://maven.aliyun.com/repository/google' } @@ -21,8 +21,8 @@ buildscript { allprojects { repositories { maven { url 'https://dl.google.com/dl/android/maven2' } - google() - jcenter() +// google() +// jcenter() maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' } maven { url 'https://maven.aliyun.com/repository/gradle-plugin' } maven { url 'https://maven.aliyun.com/repository/google' } diff --git a/study-flutter/lib/component/event/BaseEvent.dart b/study-flutter/lib/component/event/BaseEvent.dart new file mode 100644 index 000000000..4e53e9c0b --- /dev/null +++ b/study-flutter/lib/component/event/BaseEvent.dart @@ -0,0 +1,3 @@ + + +class BaseEvent {} diff --git a/study-flutter/lib/component/event/EventBus.dart b/study-flutter/lib/component/event/EventBus.dart index bd06cc88e..4db7757de 100644 --- a/study-flutter/lib/component/event/EventBus.dart +++ b/study-flutter/lib/component/event/EventBus.dart @@ -1,6 +1,6 @@ import 'package:event_bus/event_bus.dart' as marcojakob; -import 'EventListener.dart'; +typedef EventCallback = void Function(dynamic event); class EventBus { static marcojakob.EventBus _eventBus = marcojakob.EventBus(); @@ -9,10 +9,10 @@ class EventBus { _eventBus.fire(event); } - static void initConsumers() { - _eventBus.on().listen((event) { + static void consume(EventCallback callback) { + _eventBus.on().listen((event) { // All events are of type UserLoggedInEvent (or subtypes of it). - event.consume(event.data); + callback(event); }); } } diff --git a/study-flutter/lib/component/event/EventListener.dart b/study-flutter/lib/component/event/EventListener.dart deleted file mode 100644 index 20c54a8c5..000000000 --- a/study-flutter/lib/component/event/EventListener.dart +++ /dev/null @@ -1,7 +0,0 @@ -abstract class EventListener { - - dynamic data; - - void consume(dynamic data); - -} diff --git a/study-flutter/lib/startup/Application.dart b/study-flutter/lib/startup/Application.dart index a6d7dc46b..21d52db2b 100644 --- a/study-flutter/lib/startup/Application.dart +++ b/study-flutter/lib/startup/Application.dart @@ -1,7 +1,6 @@ import 'package:app_lu_lu/component/cache/HttpRequestCaches.dart'; import 'package:app_lu_lu/component/cache/TvCaches.dart'; import 'package:app_lu_lu/component/cache/UserCaches.dart'; -import 'package:app_lu_lu/component/event/EventBus.dart'; import 'package:app_lu_lu/component/http/HttpRequests.dart'; import 'package:app_lu_lu/utils/AppUtils.dart'; import 'package:flutter/cupertino.dart'; @@ -16,6 +15,5 @@ class Application { AppUtils.getDeviceId() .then((deviceId) => {UserCaches.setUserId(deviceId.hashCode)}); HttpRequests.init(); - EventBus.initConsumers(); } } diff --git a/study-flutter/lib/view/settings/FeedbackTabView.dart b/study-flutter/lib/view/settings/FeedbackTabView.dart index c91aab981..ca9b12d31 100644 --- a/study-flutter/lib/view/settings/FeedbackTabView.dart +++ b/study-flutter/lib/view/settings/FeedbackTabView.dart @@ -3,6 +3,7 @@ import 'dart:convert'; import 'package:app_lu_lu/component/cache/UserCaches.dart'; import 'package:app_lu_lu/component/dialog/Dialogs.dart'; +import 'package:app_lu_lu/component/event/EventBus.dart'; import 'package:app_lu_lu/component/http/HttpRequests.dart'; import 'package:app_lu_lu/component/log/Logs.dart'; import 'package:app_lu_lu/constant/HttpConstant.dart'; @@ -11,6 +12,7 @@ import 'package:app_lu_lu/locale/Translations.dart'; import 'package:app_lu_lu/model/AboutAuthorTab.dart'; import 'package:app_lu_lu/model/AppFeedback.dart'; import 'package:app_lu_lu/model/FeedbackQueryResult.dart'; +import 'package:app_lu_lu/view/settings/FeedbackUpdateEvent.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; @@ -67,6 +69,10 @@ class _FeedbackTabState extends State<_FeedbackTabView> { void initState() { showLoading(); _onRefresh(); + EventBus.consume((event) { + Logs.info('event = ${event.toString()}'); + _onRefresh(); + }); } @override @@ -270,6 +276,9 @@ class _FeedbackTabState extends State<_FeedbackTabView> { } Future _onRefresh() async { + if (!mounted) { + return ; + } Map paramJson = new HashMap(); paramJson.putIfAbsent("targetPage", () => 1); paramJson.putIfAbsent("pageSize", () => 100); diff --git a/study-flutter/lib/view/settings/FeedbackUpdateEvent.dart b/study-flutter/lib/view/settings/FeedbackUpdateEvent.dart index 24ea24280..234b3c842 100644 --- a/study-flutter/lib/view/settings/FeedbackUpdateEvent.dart +++ b/study-flutter/lib/view/settings/FeedbackUpdateEvent.dart @@ -1,13 +1,5 @@ -import 'package:app_lu_lu/component/event/EventListener.dart'; -import 'package:app_lu_lu/component/log/Logs.dart'; +import 'package:app_lu_lu/component/event/BaseEvent.dart'; -class FeedbackUpdateEvent implements BaseEvent { - @override - var data; - - @override - void consume(data) { - Logs.info("FeedbackUpdateEvent --- "); - Logs.info(data.toString()); - } +class FeedbackUpdateEvent extends BaseEvent { + String data = "hello world"; } diff --git a/study-flutter/lib/view/settings/Feedbacks.dart b/study-flutter/lib/view/settings/Feedbacks.dart index c81e8ea23..0aa79d716 100644 --- a/study-flutter/lib/view/settings/Feedbacks.dart +++ b/study-flutter/lib/view/settings/Feedbacks.dart @@ -43,7 +43,7 @@ class _Feedbacks extends State with SingleTickerProviderStateMixin { Logs.info('_insertFeedback responseBody=' + (result?.responseBody ?? "")); setState(() { FeedbackUpdateEvent event = FeedbackUpdateEvent(); - event.data = 1234; + event.data = "hhh"; EventBus.publish(event); }); }).catchError((error) { diff --git a/study-flutter/lib/view/tv/TvPlayer.dart b/study-flutter/lib/view/tv/TvPlayer.dart index 5f2386ac2..6e2775d9a 100644 --- a/study-flutter/lib/view/tv/TvPlayer.dart +++ b/study-flutter/lib/view/tv/TvPlayer.dart @@ -29,7 +29,7 @@ class _VideoScreenState extends State { TvItem arguments = ModalRoute.of(context)?.settings.arguments as TvItem; if (player.dataSource == null) { - player.setDataSource(arguments?.urls?[0], autoPlay: true); + player.setDataSource(arguments?.urls?.elementAt(0)??"", autoPlay: true); } return Scaffold( diff --git a/study-flutter/pubspec.lock b/study-flutter/pubspec.lock index 3c510f940..ab9ceb1ab 100644 --- a/study-flutter/pubspec.lock +++ b/study-flutter/pubspec.lock @@ -7,7 +7,7 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.6.1" + version: "2.8.1" boolean_selector: dependency: transitive description: @@ -49,7 +49,7 @@ packages: name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.3.1" clock: dependency: transitive description: @@ -175,7 +175,7 @@ packages: name: fijkplayer url: "https://pub.dartlang.org" source: hosted - version: "0.8.8" + version: "0.10.1" file: dependency: transitive description: @@ -237,7 +237,7 @@ packages: name: fluttertoast url: "https://pub.dartlang.org" source: hosted - version: "7.1.8" + version: "8.0.9" http: dependency: transitive description: @@ -279,21 +279,14 @@ packages: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.3.0" + version: "1.7.0" mime: dependency: transitive description: name: mime url: "https://pub.dartlang.org" source: hosted - version: "0.9.7" - notus: - dependency: transitive - description: - name: notus - url: "https://pub.dartlang.org" - source: hosted - version: "0.1.5" + version: "1.0.2" octo_image: dependency: transitive description: @@ -363,14 +356,14 @@ packages: name: permission_handler url: "https://pub.dartlang.org" source: hosted - version: "5.1.0+2" + version: "8.3.0" permission_handler_platform_interface: dependency: transitive description: name: permission_handler_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "2.0.2" + version: "3.7.0" photo_view: dependency: "direct main" description: @@ -391,7 +384,7 @@ packages: name: plugin_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "1.0.3" + version: "2.1.2" process: dependency: transitive description: @@ -399,20 +392,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "4.2.3" - quill_delta: - dependency: transitive - description: - name: quill_delta - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.3" - quiver_hashcode: - dependency: transitive - description: - name: quiver_hashcode - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.0" rxdart: dependency: transitive description: @@ -426,7 +405,7 @@ packages: name: share url: "https://pub.dartlang.org" source: hosted - version: "0.6.5" + version: "2.0.4" shared_preferences: dependency: "direct main" description: @@ -536,7 +515,7 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.3.0" + version: "0.4.2" timelines: dependency: "direct main" description: @@ -557,28 +536,56 @@ packages: name: url_launcher url: "https://pub.dartlang.org" source: hosted - version: "5.4.2" + version: "6.1.2" + url_launcher_android: + dependency: transitive + description: + name: url_launcher_android + url: "https://pub.dartlang.org" + source: hosted + version: "6.0.14" + url_launcher_ios: + dependency: transitive + description: + name: url_launcher_ios + url: "https://pub.dartlang.org" + source: hosted + version: "6.0.14" + url_launcher_linux: + dependency: transitive + description: + name: url_launcher_linux + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" url_launcher_macos: dependency: transitive description: name: url_launcher_macos url: "https://pub.dartlang.org" source: hosted - version: "0.0.1+9" + version: "3.0.1" url_launcher_platform_interface: dependency: transitive description: name: url_launcher_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "1.0.9" + version: "2.0.5" url_launcher_web: dependency: transitive description: name: url_launcher_web url: "https://pub.dartlang.org" source: hosted - version: "0.1.5+3" + version: "2.0.7" + url_launcher_windows: + dependency: transitive + description: + name: url_launcher_windows + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" uuid: dependency: transitive description: @@ -607,13 +614,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.2.0" - zefyr: - dependency: "direct main" - description: - name: zefyr - url: "https://pub.dartlang.org" - source: hosted - version: "0.12.0" sdks: - dart: ">=2.13.0 <3.0.0" - flutter: ">=2.2.0" + dart: ">=2.14.0 <3.0.0" + flutter: ">=2.5.0" diff --git a/study-flutter/pubspec.yaml b/study-flutter/pubspec.yaml index d7b9c3636..a0202fc30 100644 --- a/study-flutter/pubspec.yaml +++ b/study-flutter/pubspec.yaml @@ -33,7 +33,7 @@ dependencies: # https://github.com/befovy/fijkplayer - fijkplayer: ^0.8.7 + fijkplayer: ^0.10.1 # https://github.com/LaoMengFlutter/flutter-do/tree/master/flutter_app_upgrade # flutter_app_upgrade: ^1.1.0 @@ -42,7 +42,7 @@ dependencies: dio: 4.0.0 # https://pub.flutter-io.cn/packages/permission_handler/install - permission_handler: ^5.0.1+1 + permission_handler: ^8.1.3 # https://pub.flutter-io.cn/packages/path_provider path_provider: ^2.0.2 @@ -54,16 +54,16 @@ dependencies: open_file: ^3.0.3 # https://pub.flutter-io.cn/packages/fluttertoast/install - fluttertoast: ^7.1.8 + fluttertoast: ^8.0.9 - #https://pub.dev/packages/shared_preferences + # https://pub.dev/packages/shared_preferences shared_preferences: ^2.0.6 - #https://pub.flutter-io.cn/packages/url_launcher/install - url_launcher: 5.4.2 + # https://pub.flutter-io.cn/packages/url_launcher/install + url_launcher: ^6.1.2 - #https://pub.dev/packages/share/install - share: 0.6.5 + # https://pub.dev/packages/share/install + share: ^2.0.4 # https://pub.flutter-io.cn/packages/connectivity connectivity: ^3.0.6 @@ -87,7 +87,7 @@ dependencies: flutter_staggered_grid_view: ^0.4.0 # https://pub.dev/packages/zefyr/install - zefyr: ^0.12.0 + # zefyr: ^0.12.0 # https://pub.dev/packages/extended_text_field/install extended_text_field: ^8.0.0 diff --git a/study-gateway-test/pom.xml b/study-gateway-test/pom.xml index e1ac27ffb..75bb9c8b0 100644 --- a/study-gateway-test/pom.xml +++ b/study-gateway-test/pom.xml @@ -13,6 +13,8 @@ UTF-8 2.0.1.RELEASE + 1.8 + 1.8 diff --git a/study-gateway/pom.xml b/study-gateway/pom.xml index 4936e209d..3fce83b51 100644 --- a/study-gateway/pom.xml +++ b/study-gateway/pom.xml @@ -14,6 +14,8 @@ UTF-8 2.0.1.RELEASE + 1.8 + 1.8 diff --git a/study-guava/pom.xml b/study-guava/pom.xml index add009e3c..cb63a0c6b 100644 --- a/study-guava/pom.xml +++ b/study-guava/pom.xml @@ -14,6 +14,8 @@ UTF-8 2.0.1.RELEASE + 1.8 + 1.8 @@ -54,6 +56,11 @@ guava-testlib 26.0-jre + + com.github.rholder + guava-retrying + 2.0.0 + com.google.truth diff --git a/study-guava/src/main/java/com/bage/study/guava/RetryDemo.java b/study-guava/src/main/java/com/bage/study/guava/RetryDemo.java new file mode 100644 index 000000000..9dc8b4bfc --- /dev/null +++ b/study-guava/src/main/java/com/bage/study/guava/RetryDemo.java @@ -0,0 +1,53 @@ +package com.bage.study.guava; + +import com.github.rholder.retry.RetryException; +import com.github.rholder.retry.Retryer; +import com.github.rholder.retry.RetryerBuilder; +import com.github.rholder.retry.StopStrategies; +import com.google.common.base.Predicates; + +import java.io.IOException; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; + +public class RetryDemo { + + private static int count = 0; + + public static void main(String[] args) { + + Callable callable = new Callable() { + public Boolean call() throws Exception { + System.out.println(count ++); + if(count <= 2){ + throw new IOException(); + } + return true; // do something useful here + } + }; + + Retryer retryer = RetryerBuilder.newBuilder() + .retryIfResult(Predicates.isNull()) + .retryIfExceptionOfType(IOException.class) + .retryIfRuntimeException() + .withStopStrategy(StopStrategies.stopAfterAttempt(5)) + .build(); + +// Retryer retryer = RetryerBuilder.newBuilder() +// .retryIfExceptionOfType(IOException.class) +// .retryIfRuntimeException() +// .withWaitStrategy(WaitStrategies.exponentialWait(100, 5, TimeUnit.MINUTES)) +// .withStopStrategy(StopStrategies.neverStop()) +// .build(); + + try { + retryer.call(callable); + } catch (RetryException e) { + e.printStackTrace(); + } catch (ExecutionException e) { + e.printStackTrace(); + } + + + } +} diff --git a/study-hanlp/pom.xml b/study-hanlp/pom.xml index aa2400b9d..22ac121ae 100644 --- a/study-hanlp/pom.xml +++ b/study-hanlp/pom.xml @@ -17,8 +17,8 @@ UTF-8 - 1.7 - 1.7 + 1.8 + 1.8 diff --git a/study-hashids-java/pom.xml b/study-hashids-java/pom.xml index 29ac2d90d..f76fd981b 100644 --- a/study-hashids-java/pom.xml +++ b/study-hashids-java/pom.xml @@ -16,8 +16,8 @@ UTF-8 - 1.7 - 1.7 + 1.8 + 1.8 diff --git a/study-html2pdf/pom.xml b/study-html2pdf/pom.xml index 8c16a0ecf..0d41e8366 100644 --- a/study-html2pdf/pom.xml +++ b/study-html2pdf/pom.xml @@ -17,8 +17,8 @@ UTF-8 - 1.7 - 1.7 + 1.8 + 1.8 diff --git a/study-hystrix/pom.xml b/study-hystrix/pom.xml index 7d6357157..f9aed99b7 100644 --- a/study-hystrix/pom.xml +++ b/study-hystrix/pom.xml @@ -13,8 +13,8 @@ UTF-8 - 1.7 - 1.7 + 1.8 + 1.8 diff --git a/study-id-generator/pom.xml b/study-id-generator/pom.xml index 5aed39b6e..96c8e6d36 100644 --- a/study-id-generator/pom.xml +++ b/study-id-generator/pom.xml @@ -17,8 +17,8 @@ UTF-8 - 1.7 - 1.7 + 1.8 + 1.8 diff --git a/study-itext/pom.xml b/study-itext/pom.xml index cfa8a1077..22d84d440 100644 --- a/study-itext/pom.xml +++ b/study-itext/pom.xml @@ -17,8 +17,8 @@ UTF-8 - 1.7 - 1.7 + 1.8 + 1.8 @@ -37,7 +37,7 @@ net.sf.cssbox pdf2dom - 1.6 + 1.8 @@ -65,49 +65,4 @@ - - - - - - maven-clean-plugin - 3.1.0 - - - - maven-resources-plugin - 3.0.2 - - - maven-compiler-plugin - 3.8.0 - - - maven-surefire-plugin - 2.22.1 - - - maven-jar-plugin - 3.0.2 - - - maven-install-plugin - 2.5.2 - - - maven-deploy-plugin - 2.8.2 - - - - maven-site-plugin - 3.7.1 - - - maven-project-info-reports-plugin - 3.0.0 - - - - diff --git a/study-java-agent-instrument/pom.xml b/study-java-agent-instrument/pom.xml index 13ee23c79..becd6db0e 100644 --- a/study-java-agent-instrument/pom.xml +++ b/study-java-agent-instrument/pom.xml @@ -13,7 +13,7 @@ http://maven.apache.org pom - 1.0-SNAPSHOT + 0.0.1-SNAPSHOT study-java-agent-demo-service study-java-agent-transform-service diff --git a/study-java-agent-instrument/study-java-agent-attach/pom.xml b/study-java-agent-instrument/study-java-agent-attach/pom.xml index fab61b7e3..07fad242f 100644 --- a/study-java-agent-instrument/study-java-agent-attach/pom.xml +++ b/study-java-agent-instrument/study-java-agent-attach/pom.xml @@ -3,18 +3,22 @@ 4.0.0 + + study-java-agent-instrument + com.bage + 0.0.1-SNAPSHOT + - org.example + com.bage study-java-agent-attach - 1.0-SNAPSHOT study-java-agent-attach http://www.example.com UTF-8 - 1.7 - 1.7 + 1.8 + 1.8 diff --git a/study-java-agent-instrument/study-java-agent-demo-service/pom.xml b/study-java-agent-instrument/study-java-agent-demo-service/pom.xml index bccf6410c..1dcde2831 100644 --- a/study-java-agent-instrument/study-java-agent-demo-service/pom.xml +++ b/study-java-agent-instrument/study-java-agent-demo-service/pom.xml @@ -3,9 +3,9 @@ - study-java-agent - org.example - 1.0-SNAPSHOT + study-java-agent-instrument + com.bage + 0.0.1-SNAPSHOT 4.0.0 @@ -16,8 +16,8 @@ UTF-8 - 1.7 - 1.7 + 1.8 + 1.8 diff --git a/study-java-agent-instrument/study-java-agent-hotload-service/pom.xml b/study-java-agent-instrument/study-java-agent-hotload-service/pom.xml index b64d089b4..934b50c43 100644 --- a/study-java-agent-instrument/study-java-agent-hotload-service/pom.xml +++ b/study-java-agent-instrument/study-java-agent-hotload-service/pom.xml @@ -3,18 +3,22 @@ 4.0.0 + + study-java-agent-instrument + com.bage + 0.0.1-SNAPSHOT + - org.example + com.bage study-java-agent-hotload-service - 1.0-SNAPSHOT study-java-agent-hotload-service http://www.example.com UTF-8 - 1.7 - 1.7 + 1.8 + 1.8 diff --git a/study-java-agent-instrument/study-java-agent-transform-service/pom.xml b/study-java-agent-instrument/study-java-agent-transform-service/pom.xml index c3e150187..95d66559e 100644 --- a/study-java-agent-instrument/study-java-agent-transform-service/pom.xml +++ b/study-java-agent-instrument/study-java-agent-transform-service/pom.xml @@ -3,9 +3,9 @@ - study-java-agent - org.example - 1.0-SNAPSHOT + study-java-agent-instrument + com.bage + 0.0.1-SNAPSHOT 4.0.0 @@ -16,8 +16,8 @@ UTF-8 - 1.7 - 1.7 + 1.8 + 1.8 diff --git a/study-java-shell/pom.xml b/study-java-shell/pom.xml index 60ef765cd..7c0b11346 100644 --- a/study-java-shell/pom.xml +++ b/study-java-shell/pom.xml @@ -17,8 +17,8 @@ UTF-8 - 1.7 - 1.7 + 1.8 + 1.8 diff --git a/study-java/.DS_Store b/study-java/.DS_Store new file mode 100644 index 000000000..ce9e716a9 Binary files /dev/null and b/study-java/.DS_Store differ diff --git a/study-java/README.md b/study-java/README.md index 1379db1e1..328caa433 100644 --- a/study-java/README.md +++ b/study-java/README.md @@ -16,6 +16,21 @@ - 对敏感字段加密 - 序列化存储规则 +### java 线程池 + +- 参数 +- 提交过程 +- 结束过程 +- 注意事项 + +参考: 池子技术:https://mp.weixin.qq.com/s?__biz=MzUxMzQ0Njc1NQ==&mid=2247494137&idx=1&sn=85962cae0f772c2b41298dd322c6ce5a&chksm=f957aff5ce2026e3201a7b26c08cce9788b340ae371e432b13ef8d035f491cbc55ff3a6b9b79&scene=126&sessionid=1631634578&key=4f90c08c96d622750d1f94cfc9c828d2b4f9b8a4d242217acb0c892b1f5b508c3361dc32b972957612ff0fb223133a8ce49e1f65df8f4ce0aea14a1ee9521ba15fb08ac413c4a7ec31cddc52f10866e62f5854ceb9c8f6d4fc71e97d72416567c92af6c1b4572fb0e40c90d6e037f8e77fc6424650165ad3aa2d40bbe19947fa&ascene=1&uin=MjU5MTQ1MDcxMQ%3D%3D&devicetype=Windows+10+x64&version=6302019c&lang=zh_CN&exportkey=A%2FarF0N%2Fbt0u8YbyerDiyRY%3D&pass_ticket=Uc9EXPQYEBD8FJh0ig5oIlm3BsTVmcxdxPOZc%2BFgOdCZbEfmuz9f7krxzG470APE&wx_header=0&fontgear=2 + +### 阻塞队列 + +- 阻塞队列常见实现 +- 常用方法 +- 阻塞队列实现 +- 手写一个阻塞队列 怎么实现Java的序列化 diff --git a/study-java/src/main/java/com/bage/study/java/debug/DebugTest.java b/study-java/src/main/java/com/bage/study/java/debug/DebugTest.java new file mode 100644 index 000000000..5db740bb5 --- /dev/null +++ b/study-java/src/main/java/com/bage/study/java/debug/DebugTest.java @@ -0,0 +1,19 @@ +package com.bage.study.java.debug; + +public class DebugTest { + + public static void main(String[] args) { + + int n = 10; + for (int i = 0; i < n; i++) { + System.out.println(i); + // debug + // 先 选好 断点 + // 在 右键 断点 + // 设置停留条件即可 + } + System.out.println("end "); + + } + +} diff --git a/study-java/src/main/java/com/bage/study/java/java8/DurationTest.java b/study-java/src/main/java/com/bage/study/java/java8/DurationTest.java new file mode 100644 index 000000000..3c84f9caf --- /dev/null +++ b/study-java/src/main/java/com/bage/study/java/java8/DurationTest.java @@ -0,0 +1,28 @@ +package com.bage.study.java.java8; + +import java.time.Duration; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.Period; + +/** + * Java 8的新特性 + * @author bage + * + */ +public class DurationTest { + + + public static void main(String[] args) { + LocalDateTime now = LocalDateTime.now(); + LocalDate now2 = LocalDate.now(); + + System.out.println(Duration.between(now, now.plusHours(2)).toString()); + System.out.println(Duration.between(now, now.plusDays(10)).toString()); + System.out.println(Duration.between(now, now.plusMinutes(2)).toString()); + + + System.out.println(Period.between(now2, now2.plusDays(10)).toString()); + + } +} diff --git a/study-java/src/main/java/com/bage/study/java/java8/JavaStream.java b/study-java/src/main/java/com/bage/study/java/java8/JavaStream.java new file mode 100644 index 000000000..92de5eafc --- /dev/null +++ b/study-java/src/main/java/com/bage/study/java/java8/JavaStream.java @@ -0,0 +1,19 @@ +package com.bage.study.java.java8; + +import java.util.Arrays; +import java.util.stream.Collectors; + +/** + * Java 8的新特性 + * @author bage + * + */ +public class JavaStream { + + + public static void main(String[] args) { + + String hh = Arrays.asList("a","b").stream().collect(Collectors.joining(",")); + System.out.println(hh); + } +} diff --git a/study-java/src/main/java/com/bage/study/java/java8/PeriodTest.java b/study-java/src/main/java/com/bage/study/java/java8/PeriodTest.java new file mode 100644 index 000000000..75307b0f0 --- /dev/null +++ b/study-java/src/main/java/com/bage/study/java/java8/PeriodTest.java @@ -0,0 +1,19 @@ +package com.bage.study.java.java8; + +import java.time.LocalDate; +import java.time.Period; + +/** + * @author bage + * + */ +public class PeriodTest { + + + public static void main(String[] args) { + LocalDate now = LocalDate.now(); + + System.out.println(Period.between(now, now.plusDays(2)).toString()); + + } +} diff --git a/study-java/src/main/java/com/bage/study/java/multhread/MyThreadPoolExecutor.java b/study-java/src/main/java/com/bage/study/java/multhread/MyThreadPoolExecutor.java index e93f00cf3..91677a544 100644 --- a/study-java/src/main/java/com/bage/study/java/multhread/MyThreadPoolExecutor.java +++ b/study-java/src/main/java/com/bage/study/java/multhread/MyThreadPoolExecutor.java @@ -80,7 +80,8 @@ public void run() { // SynchronousQueue, // LinkedBlockingDeque, // ArrayBlockingQueue; - + +// threadPoolExecutor.shutdownNow(); } } diff --git a/study-java/src/main/java/com/bage/study/java/multhread/ThreadTest.java b/study-java/src/main/java/com/bage/study/java/multhread/ThreadTest.java new file mode 100644 index 000000000..6cb5b250c --- /dev/null +++ b/study-java/src/main/java/com/bage/study/java/multhread/ThreadTest.java @@ -0,0 +1,16 @@ +package com.bage.study.java.multhread; + +/** + * 多线程顺序执行实现 + * + * @author bage + */ +public class ThreadTest { + + public static void main(String[] args) throws Exception { + Thread thread1 = new ThreadSeq1(); + thread1.start(); + } + +} + diff --git a/study-java/src/main/java/com/bage/study/java/proxy/JdkProxyTest.java b/study-java/src/main/java/com/bage/study/java/proxy/JdkProxyTest.java new file mode 100644 index 000000000..4f437ca1e --- /dev/null +++ b/study-java/src/main/java/com/bage/study/java/proxy/JdkProxyTest.java @@ -0,0 +1,14 @@ +package com.bage.study.java.proxy; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Proxy; + +public class JdkProxyTest { + + public static void main(String[] args) { + + + + } + +} diff --git a/study-jenkins/README.md b/study-jenkins/README.md new file mode 100644 index 000000000..66aceaef1 --- /dev/null +++ b/study-jenkins/README.md @@ -0,0 +1,106 @@ +# study-Jenkins + +大纲?? + +## 参考链接 + +官网文档 https://www.jenkins.io/zh/ +https://www.jenkins.io/zh/doc/book/installing/#downloading-and-running-jenkins-in-docker +https://mp.weixin.qq.com/s/NHZLONQJTfyurUGkKjlGzg + +Docker https://hub.docker.com/r/jenkinsci/blueocean/ + +安装部署 https://www.jenkins.io/zh/doc/book/installing/ + +部署启动 + +https://www.jenkins.io/zh/doc/book/installing/ + +maven 应用: https://www.jenkins.io/zh/doc/tutorials/build-a-java-app-with-maven/ + +流水线 https://www.jenkins.io/zh/doc/book/pipeline/multibranch/ + +https://www.jenkins.io/doc/book/pipeline/multibranch/ + + + +博客参考-docker + shell : cnblogs.com/wang1221/p/16362058.html + + + +下载 + +``` +docker pull jenkinsci/blueocean + +``` + +启动 + +``` +docker run --name=bage-jenkins -p 8080:8080 -v /Users/bage/bage/docker-data/jenkins:/var/jenkins_home -v /Users/bage/bage/github:/home jenkinsci/blueocean + +``` + + + +访问 + +``` +http://localhost:8080 + +docker exec -it bage-jenkins bash + +cat /var/jenkins_home/secrets/initialAdminPassword + +拷贝密码 +dc631cefb1154ab39e54d538e99a09bd + +bage/bage123456 + +``` + + + +设置时区 + +个人中心 - 设置 - 用户定义的时区 + +``` +选择时区 + +``` + + + + + +发布 + +Publish Remote + +``` +Publish Remote + + +``` + + + + + +编译打包 + + + +``` +Publish Remote + + +``` + + + + + +https://www.jenkins.io/zh/doc/book/installing/ \ No newline at end of file diff --git a/study-jenkins/pom.xml b/study-jenkins/pom.xml new file mode 100644 index 000000000..1a0dff3d8 --- /dev/null +++ b/study-jenkins/pom.xml @@ -0,0 +1,75 @@ + + + + 4.0.0 + + com.bage + study-jenkins + 0.0.1-SNAPSHOT + + study-jenkins + + http://www.example.com + + + UTF-8 + 1.7 + 1.7 + + + + + junit + junit + 4.11 + test + + + + + + + + + maven-clean-plugin + 3.1.0 + + + + maven-resources-plugin + 3.0.2 + + + maven-compiler-plugin + 3.8.0 + + + maven-surefire-plugin + 2.22.1 + + + maven-jar-plugin + 3.0.2 + + + maven-install-plugin + 2.5.2 + + + maven-deploy-plugin + 2.8.2 + + + + maven-site-plugin + 3.7.1 + + + maven-project-info-reports-plugin + 3.0.0 + + + + + diff --git a/study-jmeter/src/main/java/com/bage/App.java b/study-jenkins/src/main/java/com/bage/App.java similarity index 100% rename from study-jmeter/src/main/java/com/bage/App.java rename to study-jenkins/src/main/java/com/bage/App.java diff --git a/study-jmeter/src/test/java/com/bage/AppTest.java b/study-jenkins/src/test/java/com/bage/AppTest.java similarity index 100% rename from study-jmeter/src/test/java/com/bage/AppTest.java rename to study-jenkins/src/test/java/com/bage/AppTest.java diff --git a/study-jmeter/README-MySQL.md b/study-jmeter/README-MySQL.md new file mode 100644 index 000000000..02b717721 --- /dev/null +++ b/study-jmeter/README-MySQL.md @@ -0,0 +1,84 @@ +# study-jmeter-MySQL # +study-jmeter-MySQL学习笔记 + +## 参考链接 ## + + + +## 简单压测 + +### 数据库压测 + +请求入口 + +http://localhost:8080/mysql/query?key=josh + +数据库连接数 + +``` +登陆 root +mysql -uroot -p + +查看当前最大连接数[默认151?] +show variables like 'max_connections'; + +设置最大连接数 +set global max_connections=10; + +查看当前连接情况 +show status like 'Threads%'; + +``` + + + + + +### 过滤结果 + +只看错误结果树 + +勾选 Error + + + +### 常见错误 + +Jmeter 报错 + +``` +java.net.SocketException: Socket closed + +``` + +解决思路 + +参考链接: https://blog.csdn.net/weixin_44898291/article/details/119023467 + +如果在 HTTP Request Sampler 的 Basic 里勾选了Use KeepAlive,那么建议在 Advanced 页签下: + +1、Implementation 选为 HttpClient4 + +2、Timeouts 中的 Connect 一般设置一个10~60秒的值,表示连接的空闲超时时间,避免由于没收到被压测端的响应回来的 Keep-Alive 的 Header 导致的连接断开 + +这个值的单位是毫秒:15s*1000=15000s + + +Jmeter 报错 + +``` +java.net.SocketException: Too many open files + +``` + +解决思路 + +参考链接: https://www.jianshu.com/p/d6f7d1557f20 + +``` +查看 +launchctl limit + +更新 +sudo launchctl limit maxfiles 4096 unlimited +``` \ No newline at end of file diff --git a/study-jmeter/README.md b/study-jmeter/README.md index cfc0d1d7e..e3cdf3007 100644 --- a/study-jmeter/README.md +++ b/study-jmeter/README.md @@ -1,8 +1,11 @@ # study-jmeter # study-jmeter学习笔记 +MySQL 压力测试: [README-MySQL.md](README-MySQL.md) + ## 参考链接 ## - 官网 [http://jmeter.apache.org/](http://jmeter.apache.org/) +- 中文网 http://www.jmeter.com.cn/ ## 环境搭建 ## 安装jdk后,下载解压直接打开 /bin/jmeter.bat @@ -68,3 +71,6 @@ KB/Sec:每秒从服务器端接收到的数据量,相当于LoadRunner中的T - JMeter Exception: java.net.BindException: Address already in use: connect [http://twit88.com/blog/2008/07/28/jmeter-exception-javanetbindexception-departmentAddress-already-in-use-connect/](http://twit88.com/blog/2008/07/28/jmeter-exception-javanetbindexception-departmentAddress-already-in-use-connect/) - Address already in use : connect 的解决办法 [https://blog.csdn.net/qq_31441637/article/details/80422901](https://blog.csdn.net/qq_31441637/article/details/80422901) + + + diff --git a/study-jmeter/jmx/jmeter-mysql.jmx b/study-jmeter/jmx/jmeter-mysql.jmx new file mode 100644 index 000000000..c6d079024 --- /dev/null +++ b/study-jmeter/jmx/jmeter-mysql.jmx @@ -0,0 +1,235 @@ + + + + + + false + true + false + + + + + + + + continue + + false + 2 + + 2000 + 1 + false + + + true + + + + + + + 127.0.0.1 + 8080 + http + + /mysql/query + GET + true + false + true + false + + HttpClient4 + 60000 + + + + + true + + saveConfig + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + true + false + false + false + true + 0 + true + true + true + true + true + true + + + + + + + false + + saveConfig + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + true + false + false + false + true + 0 + true + true + true + true + true + true + + + + + + + + continue + + false + 2 + + 1000 + 1 + false + + + true + + + + + + + 127.0.0.1 + 8080 + http + + /mysql/insert + POST + true + false + true + false + + HttpClient4 + 60000 + + + + + true + + saveConfig + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + true + false + false + false + true + 0 + true + true + true + true + true + true + + + + + + + false + + saveConfig + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + true + false + false + false + true + 0 + true + true + true + true + true + true + + + + + + + + + diff --git a/study-jmeter/jmx/jmeter-ping.jmx b/study-jmeter/jmx/jmeter-ping.jmx new file mode 100644 index 000000000..4ca95331a --- /dev/null +++ b/study-jmeter/jmx/jmeter-ping.jmx @@ -0,0 +1,206 @@ + + + + + + false + true + false + + + + + + + + continue + + false + 1 + + 1 + 1 + false + + + true + + + + + pang + + + Assertion.response_data + false + 16 + + + + false + + saveConfig + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + true + false + false + false + true + 0 + true + true + true + true + true + true + + + + + + + + + + 127.0.0.1 + 8080 + http + + /ping + GET + true + false + true + false + + + + + + + + continue + + false + 3 + + 40 + 3 + false + + + true + + + + + + + 127.0.0.1 + 8080 + http + + /mysql/query + GET + true + false + true + false + + + + + + + false + + saveConfig + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + true + false + false + false + true + 0 + true + true + true + true + true + true + + + + + + + false + + saveConfig + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + true + false + false + false + true + 0 + true + true + true + true + true + true + + + + + + + + + diff --git a/study-jmeter/pom.xml b/study-jmeter/pom.xml index f9c02bae1..a05928cb4 100644 --- a/study-jmeter/pom.xml +++ b/study-jmeter/pom.xml @@ -15,64 +15,105 @@ http://www.example.com + UTF-8 - 1.7 - 1.7 - + 1.8 + 1.8 + 2.0.1.RELEASE + + 3.4.6 + 1.3.2 + 8.0.13 + + 1.18.20 + + 1.2.12 + 1.4.0 + 1.3.0.Final + + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-test + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + + + + org.mybatis + mybatis + ${mybatis.version} + + + org.mybatis + mybatis-spring + ${mybatis-spring.version} + + + org.mybatis.spring.boot + mybatis-spring-boot-autoconfigure + ${mybatis-spring.version} + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + ${mybatis-spring.version} + + + mysql + mysql-connector-java + ${mysql.version} + + + + + org.mapstruct + mapstruct + ${org.mapstruct.version} + - junit - junit - 4.11 - test + org.mapstruct + mapstruct-jdk8 + ${org.mapstruct.version} + + + + org.projectlombok + lombok + ${lombok.version} + + + + + + + org.springframework.boot + spring-boot-dependencies + ${spring.boot.base.version} + pom + import + + + + - - - - - - maven-clean-plugin - 3.1.0 - - - - maven-resources-plugin - 3.0.2 - - - maven-compiler-plugin - 3.8.0 - - - maven-surefire-plugin - 2.22.1 - - - maven-jar-plugin - 3.0.2 - - - maven-install-plugin - 2.5.2 - - - maven-deploy-plugin - 2.8.2 - - - - maven-site-plugin - 3.7.1 - - - maven-project-info-reports-plugin - 3.0.0 - - - - diff --git a/study-jmeter/src/main/java/com/bage/study/jmeter/AppConstants.java b/study-jmeter/src/main/java/com/bage/study/jmeter/AppConstants.java new file mode 100644 index 000000000..430edd793 --- /dev/null +++ b/study-jmeter/src/main/java/com/bage/study/jmeter/AppConstants.java @@ -0,0 +1,5 @@ +package com.bage.study.jmeter; + +public class AppConstants { + public static final String APP_PREF = "jmeter"; +} diff --git a/study-jmeter/src/main/java/com/bage/study/jmeter/Application.java b/study-jmeter/src/main/java/com/bage/study/jmeter/Application.java new file mode 100644 index 000000000..5fbb9bba4 --- /dev/null +++ b/study-jmeter/src/main/java/com/bage/study/jmeter/Application.java @@ -0,0 +1,17 @@ +package com.bage.study.jmeter; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; + + +@SpringBootApplication( + scanBasePackageClasses = Application.class, + exclude = {DataSourceAutoConfiguration.class} +) +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} diff --git a/study-jmeter/src/main/java/com/bage/study/jmeter/Customer.java b/study-jmeter/src/main/java/com/bage/study/jmeter/Customer.java new file mode 100644 index 000000000..fa6100ad2 --- /dev/null +++ b/study-jmeter/src/main/java/com/bage/study/jmeter/Customer.java @@ -0,0 +1,12 @@ +package com.bage.study.jmeter; + +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class Customer { + private long id; + private String firstName, lastName; + +} \ No newline at end of file diff --git a/study-jmeter/src/main/java/com/bage/study/jmeter/PingController.java b/study-jmeter/src/main/java/com/bage/study/jmeter/PingController.java new file mode 100644 index 000000000..2010fb622 --- /dev/null +++ b/study-jmeter/src/main/java/com/bage/study/jmeter/PingController.java @@ -0,0 +1,18 @@ +package com.bage.study.jmeter; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +import java.time.LocalDateTime; + +@RestController +public class PingController { + + @RequestMapping("/ping") + public String ping() { + System.out.println(LocalDateTime.now()); + return "pang"; + } + +} diff --git a/study-jmeter/src/main/java/com/bage/study/jmeter/mysql/CustomerRepo.java b/study-jmeter/src/main/java/com/bage/study/jmeter/mysql/CustomerRepo.java new file mode 100644 index 000000000..c0e783f78 --- /dev/null +++ b/study-jmeter/src/main/java/com/bage/study/jmeter/mysql/CustomerRepo.java @@ -0,0 +1,71 @@ +package com.bage.study.jmeter.mysql; + +import com.bage.study.jmeter.Customer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +@Component +public class CustomerRepo { + private static final Logger log = LoggerFactory.getLogger(CustomerRepo.class); + @Autowired + JdbcTemplate jdbcTemplate; + +// @PostConstruct + public void init() { + + log.info("Creating tables"); + +// jdbcTemplate.execute("DROP TABLE customers IF EXISTS"); +// jdbcTemplate.execute("CREATE TABLE customers(" + +// "id SERIAL, first_name VARCHAR(255), last_name VARCHAR(255))"); + + // Split up the array of whole names into an array of first/last names + List splitUpNames = Arrays.asList("John Woo", "Jeff Dean", "Josh Bloch", "Josh Long").stream() + .map(name -> name.split(" ")) + .collect(Collectors.toList()); + + // Use a Java 8 stream to print out each tuple of the list + splitUpNames.forEach(name -> log.info(String.format("Inserting customer record for %s %s", name[0], name[1]))); + + // Uses JdbcTemplate's batchUpdate operation to bulk load data + jdbcTemplate.batchUpdate("INSERT INTO customers(first_name, last_name) VALUES (?,?)", splitUpNames); + + log.info("Querying for customer records where first_name = 'Josh':"); + jdbcTemplate.query( + "SELECT id, first_name, last_name FROM customers WHERE first_name = ?", new Object[]{"Josh"}, + (rs, rowNum) -> new Customer(rs.getLong("id"), rs.getString("first_name"), rs.getString("last_name")) + ).forEach(customer -> log.info(customer.toString())); + } + + public List query(String key) { + return jdbcTemplate.query( + "SELECT id, first_name, last_name FROM customers WHERE first_name = ?", new Object[]{key}, + (rs, rowNum) -> new Customer(rs.getLong("id"), rs.getString("first_name"), rs.getString("last_name")) + ); + } + + public int insert(List list) { + if(Objects.isNull(list)){ + return 0; + } + List splitUpNames = new ArrayList<>(); + + for (Customer customer : list) { + Object[] objs = new Object[2]; + objs[0] = customer.getFirstName(); + objs[1] = customer.getLastName(); + splitUpNames.add(objs); + } + int[] res = jdbcTemplate.batchUpdate("INSERT INTO customers(first_name, last_name) VALUES (?,?)", splitUpNames); + return Arrays.stream(res).sum(); + } +} \ No newline at end of file diff --git a/study-jmeter/src/main/java/com/bage/study/jmeter/mysql/DataSourceConfig.java b/study-jmeter/src/main/java/com/bage/study/jmeter/mysql/DataSourceConfig.java new file mode 100644 index 000000000..e63259a02 --- /dev/null +++ b/study-jmeter/src/main/java/com/bage/study/jmeter/mysql/DataSourceConfig.java @@ -0,0 +1,60 @@ +package com.bage.study.jmeter.mysql; + +import com.bage.study.jmeter.AppConstants; +import com.zaxxer.hikari.HikariDataSource; +import org.apache.ibatis.session.SqlSessionFactory; +import org.mybatis.spring.SqlSessionFactoryBean; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.jdbc.DataSourceBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.jdbc.datasource.DataSourceTransactionManager; + +import javax.sql.DataSource; + +@Configuration +@MapperScan(basePackages = "com.bage.auth.server.*.mapper", sqlSessionFactoryRef = AppConstants.APP_PREF + "SqlSessionFactory") +public class DataSourceConfig { + + + @Bean(AppConstants.APP_PREF + "DataSource") + public DataSource dataSource() { + DataSourceBuilder builder = DataSourceBuilder.create().type(HikariDataSource.class); + HikariDataSource hikariDataSource = builder + .driverClassName("com.mysql.cj.jdbc.Driver") + .url("jdbc:mysql://127.0.0.1:3306/mydb?useSSL=false&characterEncoding=utf8&allowPublicKeyRetrieval=true") + .username("bage") + .password("bage") + .build(); + hikariDataSource.setAutoCommit(true);//update自动提交设置 + hikariDataSource.setConnectionTestQuery("select 1");//连接查询语句设置 + hikariDataSource.setConnectionTimeout(5000);//连接超时时间设置 + hikariDataSource.setIdleTimeout(3000);//连接空闲生命周期设置 + hikariDataSource.setIsolateInternalQueries(false);//执行查询启动设置 + hikariDataSource.setMaximumPoolSize(2);//连接池允许的最大连接数量 + hikariDataSource.setMaxLifetime(1800000);//检查空余连接优化连接池设置时间,单位毫秒 + hikariDataSource.setMinimumIdle(2);//连接池保持最小空余连接数量 + hikariDataSource.setPoolName(AppConstants.APP_PREF + "HikariPool");//连接池名称 +// hikariDataSource.setLogWriter(); + return builder.build(); + } + + @Bean(AppConstants.APP_PREF + "SqlSessionFactory") + public SqlSessionFactory sqlSessionFactory(@Qualifier(AppConstants.APP_PREF + "DataSource") DataSource datasource) throws Exception { + SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); + Resource[] resources = new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/com/bage/auth/server/*.xml"); + sqlSessionFactoryBean.setMapperLocations(resources); + sqlSessionFactoryBean.setDataSource(datasource); + return sqlSessionFactoryBean.getObject(); + } + + @Bean(AppConstants.APP_PREF + "TransactionManager") + DataSourceTransactionManager transactionManager(@Qualifier(AppConstants.APP_PREF + "DataSource") DataSource datasource) { + return new DataSourceTransactionManager(datasource); + } + + +} diff --git a/study-jmeter/src/main/java/com/bage/study/jmeter/mysql/MySQLController.java b/study-jmeter/src/main/java/com/bage/study/jmeter/mysql/MySQLController.java new file mode 100644 index 000000000..848226818 --- /dev/null +++ b/study-jmeter/src/main/java/com/bage/study/jmeter/mysql/MySQLController.java @@ -0,0 +1,58 @@ +package com.bage.study.jmeter.mysql; + +import com.bage.study.jmeter.Customer; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Random; + +@RestController +@RequestMapping("/mysql") +public class MySQLController { + + Random random = new Random(); + @Resource + private CustomerRepo repo; + + @RequestMapping("/query") + public Object query(@RequestParam(required = false) String key) { + if (Objects.isNull(key)) { + key = String.valueOf(System.currentTimeMillis() - random.nextInt()); + } + System.out.println("query:" + LocalDateTime.now() + "; " + key); + return repo.query(key); + } + + @RequestMapping("/insert") + public Object insert(@RequestParam(value = "prefix",required = false) String prefix, + @RequestParam(value = "size",required = false) Integer size) { + if (Objects.isNull(prefix)) { + prefix = String.valueOf(System.currentTimeMillis()); + prefix = prefix.substring(prefix.length() - 10); + } + if (Objects.isNull(size)) { + size = 1; + } + List list = new ArrayList<>(); + for (int i = 0; i < size; i++) { + list.add(new Customer(i, prefix + "" + random.nextInt(100),prefix + "" + random.nextInt(100))); + } + int result = repo.insert(list); + System.out.println("insert:" + "; " + prefix + "; actual = " + result + ", expect = " + list.size()); + return result; + } + + @RequestMapping("/init") + public String init() { + repo.init(); + System.out.println("init:" + LocalDateTime.now()); + return "pang"; + } + +} diff --git a/study-jmockit/coverage-failure.txt b/study-jmockit/coverage-failure.txt new file mode 100644 index 000000000..a2c140e93 --- /dev/null +++ b/study-jmockit/coverage-failure.txt @@ -0,0 +1,17 @@ +java.lang.IllegalStateException: Running on JDK 9 requires -javaagent:/jmockit-1.n.jar or -Djdk.attach.allowAttachSelf + at mockit.internal.startup.AgentLoader.attachToRunningVM(AgentLoader.java:151) + at mockit.internal.startup.AgentLoader.loadAgent(AgentLoader.java:60) + at mockit.internal.startup.Startup.verifyInitialization(Startup.java:137) + at mockit.internal.startup.Startup.instrumentation(Startup.java:130) + at mockit.coverage.modification.ClassModification.redefineClassesAlreadyLoadedForCoverage(ClassModification.java:34) + at mockit.coverage.modification.ClassModification.(ClassModification.java:29) + at mockit.coverage.CodeCoverage.(CodeCoverage.java:52) + at mockit.coverage.CodeCoverage.create(CodeCoverage.java:90) + at mockit.internal.startup.Startup.activateCodeCoverageIfRequested(Startup.java:109) + at mockit.internal.startup.Startup.premain(Startup.java:52) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at java.instrument/sun.instrument.InstrumentationImpl.loadClassAndStartAgent(InstrumentationImpl.java:491) + at java.instrument/sun.instrument.InstrumentationImpl.loadClassAndCallPremain(InstrumentationImpl.java:503) diff --git a/study-jmockit/pom.xml b/study-jmockit/pom.xml index 66cf3872c..85130b5ae 100644 --- a/study-jmockit/pom.xml +++ b/study-jmockit/pom.xml @@ -16,8 +16,8 @@ UTF-8 - 1.7 - 1.7 + 1.8 + 1.8 diff --git a/study-jmockit/src/test/java/com/bage/jmockit/HelloJMockitTest.java b/study-jmockit/src/test/java/com/bage/jmockit/HelloJMockitTest.java index 8efe57515..bacd18e14 100644 --- a/study-jmockit/src/test/java/com/bage/jmockit/HelloJMockitTest.java +++ b/study-jmockit/src/test/java/com/bage/jmockit/HelloJMockitTest.java @@ -3,10 +3,13 @@ import mockit.Expectations; import mockit.Mocked; import mockit.Verifications; +import mockit.integration.junit4.JMockit; import org.junit.Assert; import org.junit.Test; +import org.junit.runner.RunWith; //HelloJMockit类的测试类 +@RunWith(JMockit.class) public class HelloJMockitTest { // 这是一个测试属性 diff --git a/study-jmockit/src/test/java/com/bage/jmockit/HelloServiceTest.java b/study-jmockit/src/test/java/com/bage/jmockit/HelloServiceTest.java index 479dcc931..cdda15295 100644 --- a/study-jmockit/src/test/java/com/bage/jmockit/HelloServiceTest.java +++ b/study-jmockit/src/test/java/com/bage/jmockit/HelloServiceTest.java @@ -1,31 +1,30 @@ package com.bage.jmockit; -import mockit.Expectations; -import mockit.Mocked; -import mockit.Verifications; +import mockit.*; import org.junit.Assert; import org.junit.Test; //HelloJMockit类的测试类 public class HelloServiceTest { - @Mocked + @Tested HelloService helloService; - @Mocked + @Injectable HelloJMockit helloJMockit; @Test - public void test() { + public void test(final @Mocked HelloJMockit target) { // 录制(Record) new Expectations() { { - helloJMockit.sayHello(); + target.sayHello(); // 期待上述调用的返回是"hello,david",而不是返回"hello,JMockit" result = "hello,david"; } }; // 重放(Replay) String msg = helloService.sayHello(); + System.out.println(msg); Assert.assertTrue(msg.equals("hello,david")); // 验证(Verification) new Verifications() { diff --git a/study-jol/README.md b/study-jol/README.md new file mode 100644 index 000000000..edd2a2451 --- /dev/null +++ b/study-jol/README.md @@ -0,0 +1,9 @@ +# study-jol # + +## 参考链接 ## +- Jol https://github.com/openjdk/jol + + + + + diff --git a/study-jol/pom.xml b/study-jol/pom.xml new file mode 100644 index 000000000..43cad8ce7 --- /dev/null +++ b/study-jol/pom.xml @@ -0,0 +1,84 @@ + + + + 4.0.0 + + com.bage + study-jol + 0.0.1-SNAPSHOT + + study-jol + + http://www.example.com + + + UTF-8 + 1.8 + 1.8 + 0.16 + + + + + junit + junit + 4.11 + test + + + + org.openjdk.jol + jol-core + ${jol.api.version} + + + + + + + + + + + maven-clean-plugin + 3.1.0 + + + + maven-resources-plugin + 3.0.2 + + + maven-compiler-plugin + 3.8.0 + + + maven-surefire-plugin + 2.22.1 + + + maven-jar-plugin + 3.0.2 + + + maven-install-plugin + 2.5.2 + + + maven-deploy-plugin + 2.8.2 + + + + maven-site-plugin + 3.7.1 + + + maven-project-info-reports-plugin + 3.0.0 + + + + + diff --git a/study-jol/src/main/java/com/bage/JOLSample_01_Basic.java b/study-jol/src/main/java/com/bage/JOLSample_01_Basic.java new file mode 100644 index 000000000..016bd47d9 --- /dev/null +++ b/study-jol/src/main/java/com/bage/JOLSample_01_Basic.java @@ -0,0 +1,30 @@ +package com.bage; + +import org.openjdk.jol.info.ClassLayout; +import org.openjdk.jol.vm.VM; + +import static java.lang.System.out; + +/** + * @author Aleksey Shipilev + */ +public class JOLSample_01_Basic { + + /* + * This sample showcases the basic field layout. + * You can see a few notable things here: + * a) how much the object header consumes; + * b) how fields are laid out; + * c) how the external alignment beefs up the object size + */ + + public static void main(String[] args) { + out.println(VM.current().details()); + out.println(ClassLayout.parseClass(A.class).toPrintable()); + } + + public static class A { + boolean f; + } + +} \ No newline at end of file diff --git a/study-jol/src/test/java/com/bage/AppTest.java b/study-jol/src/test/java/com/bage/AppTest.java new file mode 100644 index 000000000..9216cfd04 --- /dev/null +++ b/study-jol/src/test/java/com/bage/AppTest.java @@ -0,0 +1,20 @@ +package com.bage; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +/** + * Unit test for simple App. + */ +public class AppTest +{ + /** + * Rigorous Test :-) + */ + @Test + public void shouldAnswerWithTrue() + { + assertTrue( true ); + } +} diff --git a/study-jpush/pom.xml b/study-jpush/pom.xml index a51455cfa..50c37e854 100644 --- a/study-jpush/pom.xml +++ b/study-jpush/pom.xml @@ -14,6 +14,8 @@ http://maven.apache.org UTF-8 + 1.8 + 1.8 diff --git a/study-json-to-pojo/pom.xml b/study-json-to-pojo/pom.xml index 314ed6d9b..0b5e02bf5 100644 --- a/study-json-to-pojo/pom.xml +++ b/study-json-to-pojo/pom.xml @@ -17,8 +17,8 @@ UTF-8 - 1.7 - 1.7 + 1.8 + 1.8 @@ -35,19 +35,18 @@ 1.0.0 - - - org.json - JSON-Java - 1.2 - jar - junit junit 4.12 compile + + org.json + json + 20160810 + compile + diff --git a/study-json/pom.xml b/study-json/pom.xml index e2eb8cc6a..f28f2062b 100644 --- a/study-json/pom.xml +++ b/study-json/pom.xml @@ -29,8 +29,8 @@ UTF-8 - 1.7 - 1.7 + 1.8 + 1.8 diff --git a/study-jvm/README-dump.md b/study-jvm/README-dump.md new file mode 100644 index 000000000..6a0010ac9 --- /dev/null +++ b/study-jvm/README-dump.md @@ -0,0 +1,33 @@ +# study-jvm-dump # + +Jvm 实践笔记 + +https://article.itxueyuan.com/amLr8x +生成 dump 文件 + +## 主动生成 + + **先找到PID** + +ps -ef | grep java + + **jmap 转存快照** +jmap -dump:format=b,file=/opt/dump/test.dump {PID} + + + +## 被动生成 + + 当程序出现OutofMemory时,将会在相应的目录下生成一份dump文件,如果不指定选项HeapDumpPath则在当前目录下生成dump文件 +-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/opt/dumps + + + +## 修改内存 + +多的内存资源。两种方式分配内存资源给 MAT: +1)修改启动参数 MemoryAnalyzer.exe -vmargs -Xmx4g +2)编辑文件 MemoryAnalyzer.ini 添加 -vmargs – Xmx4g + + + diff --git a/study-jvm/pom.xml b/study-jvm/pom.xml index 32afc7868..ddec8f88c 100644 --- a/study-jvm/pom.xml +++ b/study-jvm/pom.xml @@ -32,8 +32,8 @@ UTF-8 - 1.7 - 1.7 + 1.8 + 1.8 diff --git a/study-jvm/src/main/java/SimpleDebugger.java b/study-jvm/src/main/java/SimpleDebugger.java index a8206a9ad..cf998ec44 100644 --- a/study-jvm/src/main/java/SimpleDebugger.java +++ b/study-jvm/src/main/java/SimpleDebugger.java @@ -1,3 +1,4 @@ + import com.sun.jdi.*; import com.sun.jdi.connect.Connector; import com.sun.jdi.connect.LaunchingConnector; diff --git a/study-jvm/src/main/java/com/bage/dump/GenerateDump.java b/study-jvm/src/main/java/com/bage/dump/GenerateDump.java new file mode 100644 index 000000000..182cec551 --- /dev/null +++ b/study-jvm/src/main/java/com/bage/dump/GenerateDump.java @@ -0,0 +1,10 @@ +package com.bage.dump; + +public class GenerateDump { + + public static void main(String[] args) { + + + } + +} diff --git a/study-jwt/pom.xml b/study-jwt/pom.xml index 7ab19f4c1..689e8fb04 100644 --- a/study-jwt/pom.xml +++ b/study-jwt/pom.xml @@ -14,6 +14,8 @@ UTF-8 + 1.8 + 1.8 2.0.1.RELEASE 3.4.6 1.3.2 diff --git a/study-leancloud/pom.xml b/study-leancloud/pom.xml index fccc0246b..37534cc17 100644 --- a/study-leancloud/pom.xml +++ b/study-leancloud/pom.xml @@ -17,8 +17,8 @@ UTF-8 - 1.7 - 1.7 + 1.8 + 1.8 diff --git a/study-linux/src/main/java/com/bage/linux/DMA.java b/study-linux/src/main/java/com/bage/linux/DMA.java new file mode 100644 index 000000000..cdf7ad0a0 --- /dev/null +++ b/study-linux/src/main/java/com/bage/linux/DMA.java @@ -0,0 +1,9 @@ +package com.bage.linux; + +/** + * Direct Memory Access + * + * + */ +public class DMA { +} diff --git a/study-linux/src/main/java/com/bage/linux/MMap.java b/study-linux/src/main/java/com/bage/linux/MMap.java new file mode 100644 index 000000000..e850fc9d6 --- /dev/null +++ b/study-linux/src/main/java/com/bage/linux/MMap.java @@ -0,0 +1,4 @@ +package com.bage.linux; + +public class MMap { +} diff --git a/study-linux/src/main/java/com/bage/linux/SendFile.java b/study-linux/src/main/java/com/bage/linux/SendFile.java new file mode 100644 index 000000000..43bdfc79c --- /dev/null +++ b/study-linux/src/main/java/com/bage/linux/SendFile.java @@ -0,0 +1,9 @@ +package com.bage.linux; + +/** + * https://www.cnblogs.com/fortunely/p/16211187.html + * + * + */ +public class SendFile { +} diff --git a/study-linux/src/main/java/com/bage/linux/ZeroCopy.java b/study-linux/src/main/java/com/bage/linux/ZeroCopy.java new file mode 100644 index 000000000..0fb979e17 --- /dev/null +++ b/study-linux/src/main/java/com/bage/linux/ZeroCopy.java @@ -0,0 +1,12 @@ +package com.bage.linux; + + +/** + * https://www.jianshu.com/p/028cf0008ca5 + * + * https://www.elecfans.com/d/1834816.html + * + * + */ +public class ZeroCopy { +} diff --git a/study-log-logback/README.md b/study-log-logback/README.md index 81fd25bae..600cec8a0 100644 --- a/study-log-logback/README.md +++ b/study-log-logback/README.md @@ -1,6 +1,8 @@ # study-log-logback # logback 基本使用 +todo: setRules null + ## 参考链接 ## @@ -9,7 +11,8 @@ logback 基本使用 - demo [http://github.com/qos-ch/logback-demo](http://github.com/qos-ch/logback-demo) - How to setup SLF4J and LOGBack in a web app - fast [https://wiki.base22.com/btg/how-to-setup-slf4j-and-logback-in-a-web-app-fast-35488048.html](https://wiki.base22.com/btg/how-to-setup-slf4j-and-logback-in-a-web-app-fast-35488048.html) - Mapped Diagnostic Context [https://logback.qos.ch/manual/mdc.html](https://logback.qos.ch/manual/mdc.html) - +- 日志过滤 https://www.baeldung.com/logback-mask-sensitive-data +- 日志过滤 https://github.com/premanandc/masking-logback-json-provider ## 环境搭建 ## 添加logback依赖 diff --git a/study-log-logback/pom.xml b/study-log-logback/pom.xml index a62848b14..02b3c2ed1 100644 --- a/study-log-logback/pom.xml +++ b/study-log-logback/pom.xml @@ -12,7 +12,9 @@ http://maven.apache.org UTF-8 - 1.0.13 + 1.8 + 1.8 + 1.2.3 @@ -44,5 +46,11 @@ test + + net.logstash.logback + logstash-logback-encoder + 4.11 + + diff --git a/study-log-logback/src/main/java/com/bage/study/log/logback/MaskingPatternLayout.java b/study-log-logback/src/main/java/com/bage/study/log/logback/MaskingPatternLayout.java new file mode 100644 index 000000000..3372d623b --- /dev/null +++ b/study-log-logback/src/main/java/com/bage/study/log/logback/MaskingPatternLayout.java @@ -0,0 +1,47 @@ +package com.bage.study.log.logback; + +import ch.qos.logback.classic.PatternLayout; +import ch.qos.logback.classic.spi.ILoggingEvent; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +public class MaskingPatternLayout extends PatternLayout { + + private Pattern multilinePattern; + private List maskPatterns = new ArrayList<>(); + + public void addMaskPattern(String maskPattern) { + maskPatterns.add(maskPattern); + multilinePattern = Pattern.compile(maskPatterns.stream().collect(Collectors.joining("|")), Pattern.MULTILINE); + } + +// @Override +// public String doLayout(IAccessEvent event) { +// return super.doLayout(event); +// } + + @Override + public String doLayout(ILoggingEvent event) { + return maskMessage(super.doLayout(event)); + } + private String maskMessage(String message) { + if (multilinePattern == null) { + return message; + } + StringBuilder sb = new StringBuilder(message); + Matcher matcher = multilinePattern.matcher(sb); + while (matcher.find()) { + IntStream.rangeClosed(1, matcher.groupCount()).forEach(group -> { + if (matcher.group(group) != null) { + IntStream.range(matcher.start(group), matcher.end(group)).forEach(i -> sb.setCharAt(i, '*')); + } + }); + } + return sb.toString(); + } +} diff --git a/study-log-logback/src/main/java/com/bage/study/log/logback/mask/MaskRule.java b/study-log-logback/src/main/java/com/bage/study/log/logback/mask/MaskRule.java new file mode 100644 index 000000000..81759f97a --- /dev/null +++ b/study-log-logback/src/main/java/com/bage/study/log/logback/mask/MaskRule.java @@ -0,0 +1,148 @@ +package com.bage.study.log.logback.mask; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static java.util.regex.Pattern.DOTALL; +import static java.util.regex.Pattern.MULTILINE; +import static java.util.regex.Pattern.compile; + +/** + * Rule to masks sensitive information in logs. + */ +public class MaskRule { + private final String name; + private final Pattern pattern; + private final int unmasked; + + /** + * + * @param name a friendly name for the rule. + * @param prefix a literal prefix preceding the actual search pattern. + * @param suffix a literal suffix preceding the actual search pattern. + * @param pattern a regular expression pattern to identify the personally identifiable information. + * @param unmasked the number of characters to leave unmasked. + */ + MaskRule(String name, String prefix, String suffix, String pattern, int unmasked) { + this.name = parse(name); + this.pattern = parse(prefix, suffix, pattern); + this.unmasked = unmasked; + } + + private String parse(String name) { + if (nullOrBlank(name)) { + throw new IllegalArgumentException("Name cannot be null blank!"); + } + return name.trim(); + } + + private static String repeat(String input, int times) { + if (times <= 0) return ""; + else if (times % 2 == 0) return repeat(input + input, times / 2); + else return input + repeat(input + input, times / 2); + } + + private static Pattern parse(String prefix, String suffix, String pattern) { + String parsedPrefix = nullOrBlank(prefix) ? "" : "(?<=" + prefix + ")(?:\\s*)"; + String parsedSuffix = nullOrBlank(suffix) ? "" : "(?:\\s*)(?=" + suffix + ")"; + return compile(parsedPrefix + validated(pattern) + parsedSuffix, DOTALL | MULTILINE); + } + + private static String validated(String pattern) { + if (nullOrBlank(pattern)) { + throw new IllegalArgumentException("Need a non-blank pattern value!"); + } + return pattern.startsWith("(") ? pattern : "(" + pattern + ")"; + } + + private static boolean nullOrBlank(String input) { + return input == null || "".equals(input.trim()); + } + + /** + * Applies the masking rule to the input. + * @param input the PII that needs to be masked. + * @return the masked version of the input. + */ + public String apply(String input) { + Matcher matcher = pattern.matcher(input); + if (matcher.find()) { + String match = matcher.group(1); + String mask = repeat("X", Math.min(match.length(), match.length() - unmasked)); + String replacement = mask + match.substring(mask.length()); + return input.replace(match, replacement); + } + return input; + } + + /** + * Helper to create a new rule instance. + * @see MaskRule + * @see ch.qos.logback.classic.joran.JoranConfigurator + */ + + + public static class Definition { + private String name; + private String prefix = ""; + private String suffix = ""; + private String pattern; + private int unmasked = 0; + + public Definition(String name, String pattern) { + this(name, "", "", pattern, 0); + } + + public Definition(String name, String prefix, String suffix, String pattern, int unmasked) { + this.name = name; + this.prefix = prefix; + this.suffix = suffix; + this.pattern = pattern; + this.unmasked = unmasked; + } + + public MaskRule rule() { + return new MaskRule(name, prefix, suffix, pattern, unmasked); + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getPrefix() { + return prefix; + } + + public void setPrefix(String prefix) { + this.prefix = prefix; + } + + public String getSuffix() { + return suffix; + } + + public void setSuffix(String suffix) { + this.suffix = suffix; + } + + public String getPattern() { + return pattern; + } + + public void setPattern(String pattern) { + this.pattern = pattern; + } + + public int getUnmasked() { + return unmasked; + } + + public void setUnmasked(int unmasked) { + this.unmasked = unmasked; + } + } +} \ No newline at end of file diff --git a/study-log-logback/src/main/java/com/bage/study/log/logback/mask/MaskRules.java b/study-log-logback/src/main/java/com/bage/study/log/logback/mask/MaskRules.java new file mode 100644 index 000000000..c200ec146 --- /dev/null +++ b/study-log-logback/src/main/java/com/bage/study/log/logback/mask/MaskRules.java @@ -0,0 +1,21 @@ +package com.bage.study.log.logback.mask; + +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.function.BinaryOperator; + +public class MaskRules { + private static final BinaryOperator NO_OP = (in, out) -> { + throw new UnsupportedOperationException("Only needed for parallel streams!"); + }; + + private final Set rules = new LinkedHashSet<>(); + + public void addRule(MaskRule.Definition definition) { + rules.add(definition.rule()); + } + + public String apply(String input) { + return rules.stream().reduce(input, (out, rule) -> rule.apply(out), NO_OP); + } +} \ No newline at end of file diff --git a/study-log-logback/src/main/java/com/bage/study/log/logback/mask/MaskingMessageProvider.java b/study-log-logback/src/main/java/com/bage/study/log/logback/mask/MaskingMessageProvider.java new file mode 100644 index 000000000..707bbdef6 --- /dev/null +++ b/study-log-logback/src/main/java/com/bage/study/log/logback/mask/MaskingMessageProvider.java @@ -0,0 +1,22 @@ +package com.bage.study.log.logback.mask; + +import ch.qos.logback.classic.spi.ILoggingEvent; +import com.fasterxml.jackson.core.JsonGenerator; +import net.logstash.logback.composite.JsonWritingUtils; +import net.logstash.logback.composite.loggingevent.MessageJsonProvider; + +import java.io.IOException; + +public class MaskingMessageProvider extends MessageJsonProvider { + + private MaskRules rules; + + @Override + public void writeTo(JsonGenerator generator, ILoggingEvent event) throws IOException { + JsonWritingUtils.writeStringField(generator, getFieldName(), rules.apply(event.getFormattedMessage())); + } + + public void setRules(MaskRules rules) { + this.rules = rules; + } +} \ No newline at end of file diff --git a/study-log-logback/src/main/resources/logback-test.xml b/study-log-logback/src/main/resources/logback-test.xml deleted file mode 100644 index 654a771bf..000000000 --- a/study-log-logback/src/main/resources/logback-test.xml +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - - - - - - - - %X{myTag} %d{HH:mm:ss.SSS} [%thread] %-5level %logger{32} - %msg%n - - - - - - - logFile.log - - - logFile.%d{yyyy-MM-dd_HH-mm}.log.zip - - - - - - %d{HH:mm:ss,SSS} [%thread] %-5level %logger{32} - %msg%n - - - - - - - - - - - - - - \ No newline at end of file diff --git a/study-log-logback/src/main/resources/logback.xml b/study-log-logback/src/main/resources/logback.xml index bd3d85a7d..625744c54 100644 --- a/study-log-logback/src/main/resources/logback.xml +++ b/study-log-logback/src/main/resources/logback.xml @@ -11,6 +11,40 @@ %X{myTag} %d{HH:mm:ss.SSS} [%thread] %-5level %logger{32} - %msg%n + + + + + UTC + + + + + ssn + \d{3}-?\d{2}-?\d{4} + + + + + + + { + "severity": "%level", + "thread": "%thread", + "class": "%logger{40}" + } + + + + + + + + \"ssn\"\s*:\s*\"(.*?)\" + \"email\"\s*:\s*\"(.*?)\" + + + diff --git a/study-log-logback/src/test/java/com/bage/study/log/logback/LogAppTest.java b/study-log-logback/src/test/java/com/bage/study/log/logback/LogAppTest.java index aea114717..df07015a3 100644 --- a/study-log-logback/src/test/java/com/bage/study/log/logback/LogAppTest.java +++ b/study-log-logback/src/test/java/com/bage/study/log/logback/LogAppTest.java @@ -22,4 +22,5 @@ public void test() { LOG.error("I am programming."); } + } diff --git a/study-log-logback/src/test/java/com/bage/study/log/logback/MaskLogTest.java b/study-log-logback/src/test/java/com/bage/study/log/logback/MaskLogTest.java new file mode 100644 index 000000000..f42d14e28 --- /dev/null +++ b/study-log-logback/src/test/java/com/bage/study/log/logback/MaskLogTest.java @@ -0,0 +1,28 @@ +package com.bage.study.log.logback; + +// Add the following to the imports section of your java code: +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * LogAppTest + */ +public class MaskLogTest { + + private static final Logger LOG = LoggerFactory.getLogger(MaskLogTest.class); + + + @org.junit.Test + public void testMasking() { + // Throw some logging statements in your code somewhere where you know they'll be fired right away when you run your app. For example: + String json = "{\n" + + " \"user_id\":\"87656\",\n" + + " \"ssn\":\"123-12-3453\",\n" + + " \"city\":\"Chicago\",\n" + + " \"Country\":\"U.S.\",\n" + + " \"email\":\"bage@qq.com\"\n" + + " }"; + LOG.info(json); + } + +} diff --git a/study-log-logback/src/test/resources/logback-test.xml b/study-log-logback/src/test/resources/logback-test.xml new file mode 100644 index 000000000..97c3f7b9a --- /dev/null +++ b/study-log-logback/src/test/resources/logback-test.xml @@ -0,0 +1,123 @@ + + + + + + + + + + + UTC + + + + + ssn + \d{3}-?\d{2}-?\d{4} + + + + + + + { + "severity": "%level", + "thread": "%thread", + "class": "%logger{40}" + } + + + + + + + + + + + + UTC + + + + + ssn + \d{3}-?\d{2}-?\d{4} + + + + + + + { + "severity": "%level", + "thread": "%thread", + "class": "%logger{40}" + } + + + + + + + + + + + + \"ssn\"\s*:\s*\"(.*?)\" + \"email\"\s*:\s*\"(.*?)\" + + %X{myTag} %d{HH:mm:ss.SSS} [%thread] %-5level %logger{32} - %msg%n + + + + + + + logFile.log + + + logFile.%d{yyyy-MM-dd_HH-mm}.log.zip + + + + + + %d{HH:mm:ss,SSS} [%thread] %-5level %logger{32} - %msg%n + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/study-log4j2/pom.xml b/study-log4j2/pom.xml index 79a121078..06978a3e0 100644 --- a/study-log4j2/pom.xml +++ b/study-log4j2/pom.xml @@ -12,6 +12,8 @@ http://maven.apache.org UTF-8 + 1.8 + 1.8 1.8 1.8 diff --git a/study-lombok/README.md b/study-lombok/README.md index a9b706972..947e11f03 100644 --- a/study-lombok/README.md +++ b/study-lombok/README.md @@ -5,6 +5,9 @@ lombok 学习笔记 ### 安装IDE插件 ### IDEA 插件 [https://github.com/mplushnikov/lombok-intellij-plugin](https://github.com/mplushnikov/lombok-intellij-plugin) +https://github.com/projectlombok/lombok + + - 可采用离线安装方式,从上面链接进行下载,最后进行导入 - 也可以采用在线安装方式进行安装 lombok 插件 diff --git a/study-lombok/pom.xml b/study-lombok/pom.xml index 9891c8d2e..0c674f735 100644 --- a/study-lombok/pom.xml +++ b/study-lombok/pom.xml @@ -12,6 +12,8 @@ http://maven.apache.org UTF-8 + 1.8 + 1.8 @@ -24,8 +26,8 @@ org.projectlombok lombok - 1.16.18 - provided + 1.18.20 + provided diff --git a/study-lombok/src/main/java/com/bage/study/lombok/App.java b/study-lombok/src/main/java/com/bage/study/lombok/App.java deleted file mode 100644 index 27e6f661d..000000000 --- a/study-lombok/src/main/java/com/bage/study/lombok/App.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.bage.study.lombok; - -import lombok.val; - -import java.util.HashSet; - -/** - * Hello world! - * - */ -public class App -{ - public static void main( String[] args ) - { - val sets = new HashSet(); - - sets = new HashSet(); - - System.out.println(new User("id","name").getId()); - System.out.println( "Hello World!" ); - } -} diff --git a/study-lombok/src/main/java/com/bage/study/lombok/UserAccess.java b/study-lombok/src/main/java/com/bage/study/lombok/UserAccess.java new file mode 100644 index 000000000..699dc1829 --- /dev/null +++ b/study-lombok/src/main/java/com/bage/study/lombok/UserAccess.java @@ -0,0 +1,13 @@ +package com.bage.study.lombok; + +import lombok.*; +import lombok.experimental.Accessors; + +@Data +@Accessors(fluent = true) +public class UserAccess { + + private String id; + private String name; + +} diff --git a/study-lombok/src/test/java/com/bage/study/lombok/AppTest.java b/study-lombok/src/test/java/com/bage/study/lombok/AppTest.java deleted file mode 100644 index 500356155..000000000 --- a/study-lombok/src/test/java/com/bage/study/lombok/AppTest.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.bage.study.lombok; - -import junit.framework.Test; -import junit.framework.TestCase; -import junit.framework.TestSuite; - -/** - * Unit test for simple App. - */ -public class AppTest - extends TestCase -{ - /** - * Create the test case - * - * @param testName name of the test case - */ - public AppTest( String testName ) - { - super( testName ); - } - - /** - * @return the suite of tests being tested - */ - public static Test suite() - { - return new TestSuite( AppTest.class ); - } - - /** - * Rigourous Test :-) - */ - public void testApp() - { - assertTrue( true ); - } -} diff --git a/study-lombok/src/test/java/com/bage/study/lombok/UserAccessTest.java b/study-lombok/src/test/java/com/bage/study/lombok/UserAccessTest.java new file mode 100644 index 000000000..9b8b76031 --- /dev/null +++ b/study-lombok/src/test/java/com/bage/study/lombok/UserAccessTest.java @@ -0,0 +1,14 @@ +package com.bage.study.lombok; + +import junit.framework.TestCase; + +/** + * Unit test for simple App. + */ +public class UserAccessTest extends TestCase { + public void testApp(){ + UserAccess userAccess = new UserAccess(); + userAccess.id("id").name("name"); + System.out.println(userAccess); + } +} diff --git a/study-lombok/src/test/java/com/bage/study/lombok/UserTest.java b/study-lombok/src/test/java/com/bage/study/lombok/UserTest.java new file mode 100644 index 000000000..26d8d3140 --- /dev/null +++ b/study-lombok/src/test/java/com/bage/study/lombok/UserTest.java @@ -0,0 +1,13 @@ +package com.bage.study.lombok; + +import junit.framework.TestCase; + +/** + * Unit test for simple App. + */ +public class UserTest extends TestCase { + public void testApp(){ + String id = new User().getId(); + System.out.println(id); + } +} diff --git a/study-m3u/pom.xml b/study-m3u/pom.xml index 090e62dea..5a1348fbd 100644 --- a/study-m3u/pom.xml +++ b/study-m3u/pom.xml @@ -25,8 +25,9 @@ UTF-8 - 1.7 - 1.7 + 1.8 + 1.8 + @@ -67,7 +68,7 @@ org.projectlombok lombok - 1.16.10 + 1.18.20 diff --git a/study-mapping-generator/pom.xml b/study-mapping-generator/pom.xml index 9ed5d567a..a59e55a87 100644 --- a/study-mapping-generator/pom.xml +++ b/study-mapping-generator/pom.xml @@ -16,6 +16,8 @@ UTF-8 + 1.8 + 1.8 1.7 1.7 diff --git a/study-mapstruct/README.md b/study-mapstruct/README.md new file mode 100644 index 000000000..8ba0e3b68 --- /dev/null +++ b/study-mapstruct/README.md @@ -0,0 +1,10 @@ +# study-mapstruct + +参考链接: https://mapstruct.org/documentation/installation/ + + + + + +基本使用: https://mapstruct.org/ + diff --git a/study-mapstruct/pom.xml b/study-mapstruct/pom.xml new file mode 100644 index 000000000..d4cc48a1c --- /dev/null +++ b/study-mapstruct/pom.xml @@ -0,0 +1,54 @@ + + + + + study + com.bage + 0.0.1-SNAPSHOT + + 4.0.0 + + study-mapstruct + study-mapstruct + http://www.example.com + + + 1.4.2.Final + + + + + + org.mapstruct + mapstruct + ${org.mapstruct.version} + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 1.8 + 1.8 + + + org.mapstruct + mapstruct-processor + ${org.mapstruct.version} + + + + + + + + + + + + diff --git a/study-mapstruct/src/main/java/com/bage/App.java b/study-mapstruct/src/main/java/com/bage/App.java new file mode 100644 index 000000000..933c06a77 --- /dev/null +++ b/study-mapstruct/src/main/java/com/bage/App.java @@ -0,0 +1,21 @@ +package com.bage; + +/** + * Hello world! + * + */ +public class App +{ + public static void main( String[] args ) + { + System.out.println( "Hello World!" ); + + Car car = new Car(); + car.setMake("make"); + car.setType(CarType.SEDAN); + car.setNumberOfSeats(12); + CarDto carDto = CarMapper.INSTANCE.carToCarDto(car); + + System.out.println(carDto); + } +} diff --git a/study-mapstruct/src/main/java/com/bage/Car.java b/study-mapstruct/src/main/java/com/bage/Car.java new file mode 100644 index 000000000..ddf9a6e1d --- /dev/null +++ b/study-mapstruct/src/main/java/com/bage/Car.java @@ -0,0 +1,48 @@ +package com.bage; +public class Car { + + private String make; + private int numberOfSeats; + private CarType type; + + public Car() { + } + public Car(String make, int numberOfSeats, CarType type) { + this.make = make; + this.numberOfSeats = numberOfSeats; + this.type = type; + } + + public String getMake() { + return make; + } + + public void setMake(String make) { + this.make = make; + } + + public int getNumberOfSeats() { + return numberOfSeats; + } + + public void setNumberOfSeats(int numberOfSeats) { + this.numberOfSeats = numberOfSeats; + } + + public CarType getType() { + return type; + } + + public void setType(CarType type) { + this.type = type; + } + + @Override + public String toString() { + return "Car{" + + "make='" + make + '\'' + + ", numberOfSeats=" + numberOfSeats + + ", type=" + type + + '}'; + } +} \ No newline at end of file diff --git a/study-obj-mapper/src/main/java/com/bage/CarDto.java b/study-mapstruct/src/main/java/com/bage/CarDto.java similarity index 100% rename from study-obj-mapper/src/main/java/com/bage/CarDto.java rename to study-mapstruct/src/main/java/com/bage/CarDto.java diff --git a/study-mapstruct/src/main/java/com/bage/CarMapper.java b/study-mapstruct/src/main/java/com/bage/CarMapper.java new file mode 100644 index 000000000..e4a1088d2 --- /dev/null +++ b/study-mapstruct/src/main/java/com/bage/CarMapper.java @@ -0,0 +1,23 @@ +package com.bage; + +import org.mapstruct.*; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface CarMapper { + + CarMapper INSTANCE = Mappers.getMapper( CarMapper.class ); + + @Mapping(source = "numberOfSeats", target = "seatCount") + CarDto carToCarDto(Car car); + + @ObjectFactory + default CarDto createCarDto(Car car) { + return new CarDto();// ... custom factory logic + } + + @AfterMapping + default void fillTank(Car vehicle, @MappingTarget CarDto result) { + result.setType( vehicle.getType().name()); + } +} \ No newline at end of file diff --git a/study-obj-mapper/src/main/java/com/bage/CarType.java b/study-mapstruct/src/main/java/com/bage/CarType.java similarity index 100% rename from study-obj-mapper/src/main/java/com/bage/CarType.java rename to study-mapstruct/src/main/java/com/bage/CarType.java diff --git a/study-materialize-spring-boot/pom.xml b/study-materialize-spring-boot/pom.xml index a9ea33049..b2d6e4278 100644 --- a/study-materialize-spring-boot/pom.xml +++ b/study-materialize-spring-boot/pom.xml @@ -12,6 +12,8 @@ http://maven.apache.org UTF-8 + 1.8 + 1.8 2.0.1.RELEASE 3.4.6 1.3.2 diff --git a/study-maven/src/main/java/cn/home/maventool/Main.java b/study-maven/src/main/java/cn/home/maventool/Main.java index e8e2e7946..67507bcf4 100644 --- a/study-maven/src/main/java/cn/home/maventool/Main.java +++ b/study-maven/src/main/java/cn/home/maventool/Main.java @@ -11,7 +11,6 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import sun.misc.JarFilter; /** * @author lihzh-home @@ -38,20 +37,20 @@ public static void main(String[] args) { _log.warn("The path must be a directory."); return; } - FilenameFilter filter = new JarFilter(); - File[] jarFiles = file.listFiles(filter); - for(File jar: jarFiles){ - installJarToMaven(jar); - if(isDelete){ - _log.info("Delete the original jar file ["+jar.getName()+"]."); - jar.delete(); - }else{ - if(isMove){ - String backupPath = propHelper.getValue(KEY_BACKUPPATH); - backupJar(jar,file,backupPath); - } - } - } +// FilenameFilter filter = new JarFilter(); +// File[] jarFiles = file.listFiles(filter); +// for(File jar: jarFiles){ +// installJarToMaven(jar); +// if(isDelete){ +// _log.info("Delete the original jar file ["+jar.getName()+"]."); +// jar.delete(); +// }else{ +// if(isMove){ +// String backupPath = propHelper.getValue(KEY_BACKUPPATH); +// backupJar(jar,file,backupPath); +// } +// } +// } } private static void backupJar(File jar, File file, String backupPath) { diff --git a/study-mockito/pom.xml b/study-mockito/pom.xml index 6c367c9e6..8da4dfff4 100644 --- a/study-mockito/pom.xml +++ b/study-mockito/pom.xml @@ -14,6 +14,8 @@ UTF-8 + 1.8 + 1.8 2.0.1.RELEASE diff --git a/study-mongodb/pom.xml b/study-mongodb/pom.xml index c118b2443..9cc537db3 100644 --- a/study-mongodb/pom.xml +++ b/study-mongodb/pom.xml @@ -15,6 +15,8 @@ UTF-8 + 1.8 + 1.8 2.0.1.RELEASE diff --git a/study-mybatis-generator-maven-plugin/pom.xml b/study-mybatis-generator-maven-plugin/pom.xml index d520c4ee7..88770cd5c 100644 --- a/study-mybatis-generator-maven-plugin/pom.xml +++ b/study-mybatis-generator-maven-plugin/pom.xml @@ -17,6 +17,8 @@ UTF-8 + 1.8 + 1.8 1.7 1.7 diff --git a/study-mybatis-plus/README.md b/study-mybatis-plus/README.md new file mode 100644 index 000000000..cda064341 --- /dev/null +++ b/study-mybatis-plus/README.md @@ -0,0 +1,10 @@ +# study-mybatis-plus # + +## 参考链接 ## + +MyBatis Plus 官网 + +https://baomidou.com/ + +https://github.com/baomidou/mybatis-plus-samples/tree/master/mybatis-plus-sample-quickstart + diff --git a/study-mybatis-plus/pom.xml b/study-mybatis-plus/pom.xml new file mode 100644 index 000000000..509f0fd29 --- /dev/null +++ b/study-mybatis-plus/pom.xml @@ -0,0 +1,67 @@ + + + + 4.0.0 + + com.bage + study-mybatis-plus + 0.0.1-SNAPSHOT + + study-mybatis-plus + http://www.example.com + + + UTF-8 + 2.0.1.RELEASE + 1.8 + 1.8 + + 3.5.2 + + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-log4j2 + + + org.springframework.boot + spring-boot-starter-test + + + + com.h2database + h2 + + + com.baomidou + mybatis-plus-boot-starter + ${mybatisplus.version} + + + + + + + + org.springframework.boot + spring-boot-dependencies + ${spring.boot.base.version} + pom + import + + + + + + diff --git a/study-mybatis-plus/src/main/java/com/bage/Application.java b/study-mybatis-plus/src/main/java/com/bage/Application.java new file mode 100644 index 000000000..00ab9e593 --- /dev/null +++ b/study-mybatis-plus/src/main/java/com/bage/Application.java @@ -0,0 +1,55 @@ +package com.bage; + +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.jdbc.core.JdbcTemplate; + +import javax.annotation.Resource; +import java.util.List; + +@SpringBootApplication +@MapperScan("com.bage") +public class Application implements CommandLineRunner { + + @Resource + private JdbcTemplate jdbcTemplate; + @Resource + private UserMapper userMapper; + + public static void main(String args[]) { + SpringApplication.run(Application.class, args); + } + + @Override + public void run(String... strings) throws Exception { + + jdbcTemplate.execute("DROP TABLE IF EXISTS user;\n" + + "\n" + + "CREATE TABLE user\n" + + "(\n" + + "\tid BIGINT(20) NOT NULL COMMENT '主键ID',\n" + + "\tname VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',\n" + + "\tage INT(11) NULL DEFAULT NULL COMMENT '年龄',\n" + + "\temail VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',\n" + + "\tPRIMARY KEY (id)\n" + + ");" + + ""); + + + jdbcTemplate.execute("DELETE FROM user;\n" + + "\n" + + "INSERT INTO user (id, name, age, email) VALUES\n" + + "(1, 'Jone', 18, 'test1@baomidou.com'),\n" + + "(2, 'Jack', 20, 'test2@baomidou.com'),\n" + + "(3, 'Tom', 28, 'test3@baomidou.com'),\n" + + "(4, 'Sandy', 21, 'test4@baomidou.com'),\n" + + "(5, 'Billie', 24, 'test5@baomidou.com');"); + + System.out.println(("----- selectAll method test ------")); + List userList = userMapper.selectList(null); + System.out.println(("----- selectAll size ------" + userList.size())); + userList.forEach(System.out::println); + } +} \ No newline at end of file diff --git a/study-mybatis-plus/src/main/java/com/bage/User.java b/study-mybatis-plus/src/main/java/com/bage/User.java new file mode 100644 index 000000000..644c49e8b --- /dev/null +++ b/study-mybatis-plus/src/main/java/com/bage/User.java @@ -0,0 +1,50 @@ +package com.bage; + +public class User { + private Long id; + private String name; + private Integer age; + private String email; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Integer getAge() { + return age; + } + + public void setAge(Integer age) { + this.age = age; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + @Override + public String toString() { + return "User{" + + "id=" + id + + ", name='" + name + '\'' + + ", age=" + age + + ", email='" + email + '\'' + + '}'; + } +} \ No newline at end of file diff --git a/study-mybatis-plus/src/main/java/com/bage/UserMapper.java b/study-mybatis-plus/src/main/java/com/bage/UserMapper.java new file mode 100644 index 000000000..88e50a7c1 --- /dev/null +++ b/study-mybatis-plus/src/main/java/com/bage/UserMapper.java @@ -0,0 +1,7 @@ +package com.bage; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +public interface UserMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/study-mybatis-plus/src/main/resources/application.yml b/study-mybatis-plus/src/main/resources/application.yml new file mode 100644 index 000000000..b256840a2 --- /dev/null +++ b/study-mybatis-plus/src/main/resources/application.yml @@ -0,0 +1,23 @@ + +server: + port: 8002 + +# DataSource Config +spring: + datasource: + driver-class-name: org.h2.Driver + sql: + init: + schema-locations: classpath:db-init/schema-h2.sql + data-locations: classpath:db-init/data-h2.sql + # schema: classpath:db-init/schema-h2.sql +# data: classpath:db-init/data-h2.sql + initialization-mode: always + url: jdbc:h2:mem:test + username: root + password: test + +# Logger Config +logging: + level: + com.bage: debug \ No newline at end of file diff --git a/study-mybatis-plus/src/main/resources/db-init/data-h2.sql b/study-mybatis-plus/src/main/resources/db-init/data-h2.sql new file mode 100644 index 000000000..a4bc3377a --- /dev/null +++ b/study-mybatis-plus/src/main/resources/db-init/data-h2.sql @@ -0,0 +1,8 @@ +DELETE FROM user; + +INSERT INTO user (id, name, age, email) VALUES +(1, 'Jone', 18, 'test1@baomidou.com'), +(2, 'Jack', 20, 'test2@baomidou.com'), +(3, 'Tom', 28, 'test3@baomidou.com'), +(4, 'Sandy', 21, 'test4@baomidou.com'), +(5, 'Billie', 24, 'test5@baomidou.com'); \ No newline at end of file diff --git a/study-mybatis-plus/src/main/resources/db-init/schema-h2.sql b/study-mybatis-plus/src/main/resources/db-init/schema-h2.sql new file mode 100644 index 000000000..baa5a6160 --- /dev/null +++ b/study-mybatis-plus/src/main/resources/db-init/schema-h2.sql @@ -0,0 +1,10 @@ +DROP TABLE IF EXISTS user; + +CREATE TABLE user +( + id BIGINT(20) NOT NULL COMMENT '主键ID', + name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名', + age INT(11) NULL DEFAULT NULL COMMENT '年龄', + email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱', + PRIMARY KEY (id) +); \ No newline at end of file diff --git a/study-mybatis/pom.xml b/study-mybatis/pom.xml index 5004e0322..c4b247daf 100644 --- a/study-mybatis/pom.xml +++ b/study-mybatis/pom.xml @@ -99,7 +99,7 @@ org.projectlombok lombok - 1.16.18 + 1.18.20 provided @@ -117,7 +117,7 @@ - + diff --git a/study-mybatis/src/main/java/com/bage/study/mybatis/springboot/org/controller/UserController.java b/study-mybatis/src/main/java/com/bage/study/mybatis/springboot/org/controller/UserController.java index 9f210be02..15fae5aa6 100644 --- a/study-mybatis/src/main/java/com/bage/study/mybatis/springboot/org/controller/UserController.java +++ b/study-mybatis/src/main/java/com/bage/study/mybatis/springboot/org/controller/UserController.java @@ -1,12 +1,17 @@ package com.bage.study.mybatis.springboot.org.controller; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import com.bage.study.mybatis.springboot.org.dao.UserExtMapper; import com.bage.study.mybatis.springboot.org.dao.UserMapper; import com.bage.study.mybatis.springboot.org.domain.Sex; import com.bage.study.mybatis.springboot.org.domain.User; import com.github.pagehelper.PageHelper; +import org.apache.ibatis.session.ExecutorType; +import org.apache.ibatis.session.SqlSession; +import org.mybatis.spring.SqlSessionTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @@ -18,6 +23,10 @@ public class UserController { @Autowired UserMapper userMapper; + @Autowired + UserExtMapper userExtMapper; + @Autowired + SqlSessionTemplate sqlSessionTemplate; @RequestMapping("/insert") @ResponseBody @@ -34,27 +43,66 @@ public Integer insert() { return res; } - @RequestMapping("/batchInsert") + @RequestMapping("/batchInsert2") @ResponseBody - public Integer batchInsert() { - User user2 = new User(); - user2.setId(3434L); - user2.setName("zhangsan33"); - user2.setSex(Sex.Male); - user2.setDepartmentId(3L); + public Long batchInsert2() { + long start = System.currentTimeMillis(); + List users = new ArrayList<>(); + SqlSession sqlSession = sqlSessionTemplate.getSqlSessionFactory().openSession(ExecutorType.BATCH, false); - User user = new User(); - user.setId(2L); - user.setName("zhangsan"); - user.setSex(Sex.Male); - user.setDepartmentId(1L); + UserMapper mapper = sqlSession.getMapper(UserMapper.class); + for (int i = 0; i < 10000; i++) { + User user2 = new User(); + user2.setId((long) (1000 + i)); + user2.setName("zhangsan" + i); + user2.setSex(Sex.Male); + user2.setDepartmentId(3L); - List users = Arrays.asList(user, user2); - int res = userMapper.batchInsert(users); + int res = mapper.insert(user2); + System.out.println("验证主键回写::" + res); - System.out.println("验证主键回写::" + users); + } + sqlSession.commit(); - return res; + return System.currentTimeMillis() - start; + } + + @RequestMapping("/batchInsert3") + @ResponseBody + public Long batchInsert3() { + long start = System.currentTimeMillis(); + List users = new ArrayList<>(); + for (int i = 0; i < 10000; i++) { + User user2 = new User(); + user2.setId((long) (1000 + i)); + user2.setName("zhangsan" + i); + user2.setSex(Sex.Male); + user2.setDepartmentId(3L); + + int res = userMapper.insert(user2); + System.out.println("验证主键回写::" + res); + + } + return System.currentTimeMillis() - start; + } + + @RequestMapping("/batchInsert") + @ResponseBody + public Long batchInsert() { + long start = System.currentTimeMillis(); + List users = new ArrayList<>(); + for (int i = 0; i < 10000; i++) { + User user2 = new User(); + user2.setId((long) (1000 + i)); + user2.setName("zhangsan" + i); + user2.setSex(Sex.Male); + user2.setDepartmentId(3L); + + users.add(user2); + } + int res = userMapper.batchInsert(users); + System.out.println("验证主键回写::" + users); + return System.currentTimeMillis() - start; } @RequestMapping("/delete") @@ -80,6 +128,14 @@ public List queryByDepartmentId() { long departmentId = 1L; return userMapper.queryByDepartmentId(departmentId); } + + @RequestMapping("/queryByDepartmentId2") + @ResponseBody + public List queryByDepartmentId2() { + long departmentId = 1L; + return userExtMapper.queryByDepartmentId2(departmentId); + } + @RequestMapping("/all") @ResponseBody public List getUser() { diff --git a/study-mybatis/src/main/java/com/bage/study/mybatis/springboot/org/dao/UserExtMapper.java b/study-mybatis/src/main/java/com/bage/study/mybatis/springboot/org/dao/UserExtMapper.java new file mode 100644 index 000000000..40560d15e --- /dev/null +++ b/study-mybatis/src/main/java/com/bage/study/mybatis/springboot/org/dao/UserExtMapper.java @@ -0,0 +1,13 @@ +package com.bage.study.mybatis.springboot.org.dao; + +import com.bage.study.mybatis.springboot.org.domain.User; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface UserExtMapper extends UserMapper { + + List queryByDepartmentId2(long departmentId); + +} diff --git a/study-mybatis/src/main/resources/com/bage/study/mybatis/springboot/org/UserExtMapper.xml b/study-mybatis/src/main/resources/com/bage/study/mybatis/springboot/org/UserExtMapper.xml new file mode 100644 index 000000000..be0ef1509 --- /dev/null +++ b/study-mybatis/src/main/resources/com/bage/study/mybatis/springboot/org/UserExtMapper.xml @@ -0,0 +1,25 @@ + + + + + + + + \ No newline at end of file diff --git a/study-mybatis/src/main/resources/mybatis-config.xml b/study-mybatis/src/main/resources/mybatis-config.xml index a29bbeace..fa3d0e9b5 100644 --- a/study-mybatis/src/main/resources/mybatis-config.xml +++ b/study-mybatis/src/main/resources/mybatis-config.xml @@ -39,6 +39,7 @@ + diff --git a/study-nginx/README.md b/study-nginx/README.md new file mode 100644 index 000000000..152473e0c --- /dev/null +++ b/study-nginx/README.md @@ -0,0 +1,111 @@ +# Study-nginx # + + + + +## 参考链接 ## +- 官网文档 http://nginx.org/en/docs/ +- docker hub 下载 https://hub.docker.com/_/nginx +- Loadbalance http://nginx.org/en/docs/http/load_balancing.html +- 负载均衡模块 http://nginx.org/en/docs/http/ngx_http_upstream_module.html +- URL 重写 http://nginx.org/en/docs/http/converting_rewrite_rules.html +- https://www.bilibili.com/video/BV1em4y1A7ey/?spm_id_from=333.1007.tianma.9-2-38.click&vd_source=72424c3da68577f00ea40a9e4f9001a1 + + + +## Nginx + +To start nginx, run the executable file. Once nginx is started, it can be controlled by invoking the executable with the `-s` parameter. Use the following syntax: + +> ``` +> nginx -s signal +> ``` + +Where *signal* may be one of the following: + +- `stop` — fast shutdown +- `quit` — graceful shutdown +- `reload` — reloading the configuration file +- `reopen` — reopening the log files + + + +Example + +> ``` +> nginx -s reload +> ``` + + + +For getting the list of all running nginx processes + +> ``` +> ps -ax | grep nginx +> ``` + + + +## Docker + + + +``` +docker pull nginx +``` + + + +```console +docker run --name bage-nginx -d -p 8080:80 nginx +``` + + + +## Conf + +server + + + + + +http://nginx.org/en/docs/http/load_balancing.html + +weight + +``` +http { + upstream myapp1 { + server srv1.example.com weight=3; + server srv2.example.com; + server srv3.example.com; + } + + server { + listen 80; + + location / { + proxy_pass http://myapp1; + } + } +} +``` + + + +## Converting rewrite rules + +``` +RewriteCond %{HTTP_HOST} example.org +RewriteRule (.*) http://www.example.org$1 +``` + +大纲 + +1、 反向代理 + +2、 配置文件 + +3、 为何?Nginx 架构 + diff --git a/study-nginx/pom.xml b/study-nginx/pom.xml new file mode 100644 index 000000000..7c4a46ffb --- /dev/null +++ b/study-nginx/pom.xml @@ -0,0 +1,78 @@ + + + + + study + com.bage + 0.0.1-SNAPSHOT + + 4.0.0 + + study-nginx + + study-nginx + + http://www.example.com + + + UTF-8 + 1.7 + 1.7 + + + + + junit + junit + 4.11 + test + + + + + + + + + maven-clean-plugin + 3.1.0 + + + + maven-resources-plugin + 3.0.2 + + + maven-compiler-plugin + 3.8.0 + + + maven-surefire-plugin + 2.22.1 + + + maven-jar-plugin + 3.0.2 + + + maven-install-plugin + 2.5.2 + + + maven-deploy-plugin + 2.8.2 + + + + maven-site-plugin + 3.7.1 + + + maven-project-info-reports-plugin + 3.0.0 + + + + + diff --git a/study-nginx/src/main/java/com/bage/App.java b/study-nginx/src/main/java/com/bage/App.java new file mode 100644 index 000000000..65b45b532 --- /dev/null +++ b/study-nginx/src/main/java/com/bage/App.java @@ -0,0 +1,13 @@ +package com.bage; + +/** + * Hello world! + * + */ +public class App +{ + public static void main( String[] args ) + { + System.out.println( "Hello World!" ); + } +} diff --git a/study-nginx/src/test/java/com/bage/AppTest.java b/study-nginx/src/test/java/com/bage/AppTest.java new file mode 100644 index 000000000..9216cfd04 --- /dev/null +++ b/study-nginx/src/test/java/com/bage/AppTest.java @@ -0,0 +1,20 @@ +package com.bage; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +/** + * Unit test for simple App. + */ +public class AppTest +{ + /** + * Rigorous Test :-) + */ + @Test + public void shouldAnswerWithTrue() + { + assertTrue( true ); + } +} diff --git a/study-obj-mapper/pom.xml b/study-obj-mapper/pom.xml index 6afeb9cba..2510aa10d 100644 --- a/study-obj-mapper/pom.xml +++ b/study-obj-mapper/pom.xml @@ -17,9 +17,9 @@ UTF-8 - 1.7 - 1.7 - 1.3.0.Final + 1.8 + 1.8 + 1.4.2.Final @@ -35,14 +35,14 @@ mapstruct ${org.mapstruct.version} + - org.assertj - assertj-core - 3.9.1 - test + org.projectlombok + lombok + 1.18.16 + provided - @@ -55,11 +55,20 @@ 1.8 1.8 + + + org.projectlombok + lombok + 1.18.16 + + org.mapstruct mapstruct-processor ${org.mapstruct.version} + + diff --git a/study-obj-mapper/src/main/java/com/bage/AppTest.java b/study-obj-mapper/src/main/java/com/bage/AppTest.java new file mode 100644 index 000000000..84d4aeff2 --- /dev/null +++ b/study-obj-mapper/src/main/java/com/bage/AppTest.java @@ -0,0 +1,20 @@ +package com.bage; + +/** + * Unit test for simple App. + */ +public class AppTest { + public static void main(String[] args) { + //given + Car car = new Car(); + + car.setMake("mkae"); + car.setNumberOfSeats(4); + + //when + Car2 carDto = CarMapper.INSTANCE.carToCarDto(car); + + //then + System.out.println(carDto); + } +} diff --git a/study-obj-mapper/src/main/java/com/bage/Car.java b/study-obj-mapper/src/main/java/com/bage/Car.java index ddf9a6e1d..0acae5ff0 100644 --- a/study-obj-mapper/src/main/java/com/bage/Car.java +++ b/study-obj-mapper/src/main/java/com/bage/Car.java @@ -1,48 +1,13 @@ package com.bage; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data public class Car { private String make; private int numberOfSeats; - private CarType type; - - public Car() { - } - public Car(String make, int numberOfSeats, CarType type) { - this.make = make; - this.numberOfSeats = numberOfSeats; - this.type = type; - } - - public String getMake() { - return make; - } - - public void setMake(String make) { - this.make = make; - } - - public int getNumberOfSeats() { - return numberOfSeats; - } - - public void setNumberOfSeats(int numberOfSeats) { - this.numberOfSeats = numberOfSeats; - } - - public CarType getType() { - return type; - } - - public void setType(CarType type) { - this.type = type; - } - @Override - public String toString() { - return "Car{" + - "make='" + make + '\'' + - ", numberOfSeats=" + numberOfSeats + - ", type=" + type + - '}'; - } } \ No newline at end of file diff --git a/study-obj-mapper/src/main/java/com/bage/Car2.java b/study-obj-mapper/src/main/java/com/bage/Car2.java new file mode 100644 index 000000000..fdbf4835c --- /dev/null +++ b/study-obj-mapper/src/main/java/com/bage/Car2.java @@ -0,0 +1,13 @@ +package com.bage; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +public class Car2 { + + private String make; + private int numberOfSeats; + +} \ No newline at end of file diff --git a/study-obj-mapper/src/main/java/com/bage/CarMapper.java b/study-obj-mapper/src/main/java/com/bage/CarMapper.java index e4a1088d2..07fa6d7f8 100644 --- a/study-obj-mapper/src/main/java/com/bage/CarMapper.java +++ b/study-obj-mapper/src/main/java/com/bage/CarMapper.java @@ -8,16 +8,6 @@ public interface CarMapper { CarMapper INSTANCE = Mappers.getMapper( CarMapper.class ); - @Mapping(source = "numberOfSeats", target = "seatCount") - CarDto carToCarDto(Car car); + Car2 carToCarDto(Car car); - @ObjectFactory - default CarDto createCarDto(Car car) { - return new CarDto();// ... custom factory logic - } - - @AfterMapping - default void fillTank(Car vehicle, @MappingTarget CarDto result) { - result.setType( vehicle.getType().name()); - } } \ No newline at end of file diff --git a/study-obj-mapper/src/main/test/com/bage/AppTest.java b/study-obj-mapper/src/main/test/com/bage/AppTest.java deleted file mode 100644 index 237ef510a..000000000 --- a/study-obj-mapper/src/main/test/com/bage/AppTest.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.bage; - -import org.assertj.core.api.Assertions; -import org.junit.Test; - -/** - * Unit test for simple App. - */ -public class AppTest -{ - @Test - public void shouldMapCarToDto() { - //given - Car car = new Car( "Morris", 5, CarType.SEDAN ); - - //when - CarDto carDto = CarMapper.INSTANCE.carToCarDto( car ); - - //then - Assertions.assertThat( carDto ).isNotNull(); - Assertions.assertThat( carDto.getMake() ).isEqualTo( "Morris" ); - Assertions.assertThat( carDto.getSeatCount() ).isEqualTo( 5 ); - Assertions.assertThat( carDto.getType() ).isEqualTo( "SEDAN" ); - } -} diff --git a/study-oceanbase/README.md b/study-oceanbase/README.md new file mode 100644 index 000000000..bb794140f --- /dev/null +++ b/study-oceanbase/README.md @@ -0,0 +1,14 @@ + +# study-ocean base + + +参考链接: + +https://open.oceanbase.com/docs/observer-cn/V3.1.3/10000000000096649 + + + +Docker + +https://open.oceanbase.com/docs/observer-cn/V3.1.3/10000000000096600 + diff --git a/study-oceanbase/pom.xml b/study-oceanbase/pom.xml new file mode 100644 index 000000000..0c4286a06 --- /dev/null +++ b/study-oceanbase/pom.xml @@ -0,0 +1,78 @@ + + + + + study + com.bage + 0.0.1-SNAPSHOT + + 4.0.0 + + study-oceanbase + + study-oceanbase + + http://www.example.com + + + UTF-8 + 1.7 + 1.7 + + + + + junit + junit + 4.11 + test + + + + + + + + + maven-clean-plugin + 3.1.0 + + + + maven-resources-plugin + 3.0.2 + + + maven-compiler-plugin + 3.8.0 + + + maven-surefire-plugin + 2.22.1 + + + maven-jar-plugin + 3.0.2 + + + maven-install-plugin + 2.5.2 + + + maven-deploy-plugin + 2.8.2 + + + + maven-site-plugin + 3.7.1 + + + maven-project-info-reports-plugin + 3.0.0 + + + + + diff --git a/study-oceanbase/src/main/java/com/bage/App.java b/study-oceanbase/src/main/java/com/bage/App.java new file mode 100644 index 000000000..65b45b532 --- /dev/null +++ b/study-oceanbase/src/main/java/com/bage/App.java @@ -0,0 +1,13 @@ +package com.bage; + +/** + * Hello world! + * + */ +public class App +{ + public static void main( String[] args ) + { + System.out.println( "Hello World!" ); + } +} diff --git a/study-oceanbase/src/test/java/com/bage/AppTest.java b/study-oceanbase/src/test/java/com/bage/AppTest.java new file mode 100644 index 000000000..9216cfd04 --- /dev/null +++ b/study-oceanbase/src/test/java/com/bage/AppTest.java @@ -0,0 +1,20 @@ +package com.bage; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +/** + * Unit test for simple App. + */ +public class AppTest +{ + /** + * Rigorous Test :-) + */ + @Test + public void shouldAnswerWithTrue() + { + assertTrue( true ); + } +} diff --git a/study-quartz/pom.xml b/study-quartz/pom.xml index 43b0ce59f..5e9d65afd 100644 --- a/study-quartz/pom.xml +++ b/study-quartz/pom.xml @@ -71,6 +71,7 @@ org.projectlombok lombok + 1.18.20 true diff --git a/study-redis/README.md b/study-redis/README.md index 628d476a4..e7beef239 100644 --- a/study-redis/README.md +++ b/study-redis/README.md @@ -84,7 +84,7 @@ redis采用的是定期删除+惰性删除策略 192.168.127.128:6379>exists name age departmentAddress(该值存在) (integer)2 - + 192.168.127.128:6379>exists name1 age1 departmentAddress(该值存在) (integer)0 - 语法:flushdb,清空当前数据所有的键值对。 @@ -140,7 +140,7 @@ redis采用的是定期删除+惰性删除策略 port 6379 4. 绑定的主机地址 bind 127.0.0.1 -5.当 客户端闲置多长时间后关闭连接,如果指定为0,表示关闭该功能 + 5.当 客户端闲置多长时间后关闭连接,如果指定为0,表示关闭该功能 timeout 300 6. 指定日志记录级别,Redis总共支持四个级别:debug、verbose、notice、warning,默认为verbose loglevel verbose @@ -155,7 +155,7 @@ redis采用的是定期删除+惰性删除策略 save 300 10 save 60 10000 分别表示900秒(15分钟)内有1个更改,300秒(5分钟)内有10个更改以及60秒内有10000个更改。 - + 10. 指定存储至本地数据库时是否压缩数据,默认为yes,Redis采用LZF压缩,如果为了节省CPU时间,可以关闭该选项,但会导致数据库文件变的巨大 rdbcompression yes 11. 指定本地数据库文件名,默认值为dump.rdb @@ -181,7 +181,7 @@ redis采用的是定期删除+惰性删除策略 always:表示每次更新操作后手动调用fsync()将数据写到磁盘(慢,安全) everysec:表示每秒同步一次(折衷,默认值) appendfsync everysec - + 21. 指定是否启用虚拟内存机制,默认值为no,简单的介绍一下,VM机制将数据分页存放,由Redis将访问量较少的页即冷数据swap到磁盘上,访问多的页面由磁盘自动换出到内存中(在后面的文章我会仔细分析Redis的VM机制) vm-enabled no 22. 虚拟内存文件路径,默认值为/tmp/redis.swap,不可多个Redis实例共享 @@ -233,3 +233,30 @@ Codis一个比较大的优点是可以不停机动态新增或删除数据节点 客户端分片 +## 慢查询日志 ## + +### 设置慢日志时间阈值 + +### 查询慢查询日志 + +### 扫描大Key + +需要注意的是当我们在线上实例进行大key扫描时,Redis的QPS会突增,为了降低扫描过程中对Redis的影响,我们需要控制扫描的频率,使用-i参数控制即可,它表示扫描过程中每次扫描的时间间隔,单位是秒。 + +### 大量key集中过期 + +有时你会发现,平时在使用Redis时没有延时比较大的情况,但在某个时间点突然出现一波延时,而且报慢的时间点很有规律,例如某个整点,或者间隔多久就会发生一次。 + +如果出现这种情况,就需要考虑是否存在大量key集中过期的情况 + +### **实例内存达到上限** + +实例的内存达到了maxmemory后,你会发现之后的每次写入新的数据,有可能变慢了。 + +导致变慢的原因是,当Redis内存达到maxmemory后,每次写入新的数据之前,必须先踢出一部分数据,让内存维持在maxmemory之下。 + +这个踢出旧数据的逻辑也是需要消耗时间的,而具体耗时的长短,要取决于配置的淘汰策略: + +### **fork耗时严重** + +执行生成RDB和AOF重写任务导致的。 diff --git a/study-regex/pom.xml b/study-regex/pom.xml index bcdeefccd..328468e33 100644 --- a/study-regex/pom.xml +++ b/study-regex/pom.xml @@ -17,8 +17,8 @@ UTF-8 - 1.7 - 1.7 + 1.8 + 1.8 diff --git a/study-resilience4j/README.md b/study-resilience4j/README.md index bbe7cb565..e4778e19c 100644 --- a/study-resilience4j/README.md +++ b/study-resilience4j/README.md @@ -3,4 +3,6 @@ resilience4j笔记 ## 参考链接 ## -- [https://github.com/resilience4j/resilience4j](https://github.com/resilience4j/resilience4j) \ No newline at end of file + +https://github.com/resilience4j/resilience4j + diff --git a/study-resilience4j/src/main/java/com/bage/App.java b/study-resilience4j/src/main/java/com/bage/App.java index 65b45b532..e2a7f39b7 100644 --- a/study-resilience4j/src/main/java/com/bage/App.java +++ b/study-resilience4j/src/main/java/com/bage/App.java @@ -1,13 +1,71 @@ package com.bage; +import io.github.resilience4j.bulkhead.Bulkhead; +import io.github.resilience4j.bulkhead.ThreadPoolBulkhead; +import io.github.resilience4j.circuitbreaker.CallNotPermittedException; +import io.github.resilience4j.circuitbreaker.CircuitBreaker; +import io.github.resilience4j.retry.Retry; +import io.github.resilience4j.timelimiter.TimeLimiter; + +import java.time.Duration; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeoutException; +import java.util.function.Supplier; + /** * Hello world! - * */ -public class App -{ - public static void main( String[] args ) - { - System.out.println( "Hello World!" ); +public class App { + public static void main(String[] args) { + +// Create a CircuitBreaker with default configuration + CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("backendService"); + +// Create a Retry with default configuration +// 3 retry attempts and a fixed time interval between retries of 500ms + Retry retry = Retry.ofDefaults("backendService"); + +// Create a Bulkhead with default configuration + Bulkhead bulkhead = Bulkhead.ofDefaults("backendService"); + + Supplier supplier = () -> backendService + .doSomething(param1, param2); + +// Decorate your call to backendService.doSomething() +// with a Bulkhead, CircuitBreaker and Retry +// **note: you will need the resilience4j-all dependency for this + Supplier decoratedSupplier = Decorators.ofSupplier(supplier) + .withCircuitBreaker(circuitBreaker) + .withBulkhead(bulkhead) + .withRetry(retry) + .decorate(); + +// Execute the decorated supplier and recover from any exception + String result = Try.ofSupplier(decoratedSupplier) + .recover(throwable -> "Hello from Recovery").get(); + +// When you don't want to decorate your lambda expression, +// but just execute it and protect the call by a CircuitBreaker. + String result = circuitBreaker + .executeSupplier(backendService::doSomething); + +// You can also run the supplier asynchronously in a ThreadPoolBulkhead + ThreadPoolBulkhead threadPoolBulkhead = ThreadPoolBulkhead + .ofDefaults("backendService"); + +// The Scheduler is needed to schedule a timeout on a non-blocking CompletableFuture + ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(3); + TimeLimiter timeLimiter = TimeLimiter.of(Duration.ofSeconds(1)); + + CompletableFuture future = Decorators.ofSupplier(supplier) + .withThreadPoolBulkhead(threadPoolBulkhead) + .withTimeLimiter(timeLimiter, scheduler) + .withCircuitBreaker(circuitBreaker) + .withFallback(asList(TimeoutException.class, CallNotPermittedException.class, BulkheadFullException.class), + throwable -> "Hello from Recovery") + .get().toCompletableFuture(); + } } diff --git a/study-resilience4j/src/main/java/com/bage/BackendService.java b/study-resilience4j/src/main/java/com/bage/BackendService.java new file mode 100644 index 000000000..797e2faf5 --- /dev/null +++ b/study-resilience4j/src/main/java/com/bage/BackendService.java @@ -0,0 +1,6 @@ +package com.bage; + +// Simulates a Backend Service +public interface BackendService { + String doSomething(); +} diff --git a/study-security/README-security.md b/study-security/README-security.md index 54ee88133..0b33c51ee 100644 --- a/study-security/README-security.md +++ b/study-security/README-security.md @@ -1,8 +1,6 @@ # Web Security # -Web 应用安全 - -很重要!!! +Web 应用安全,,,很重要!!! ## 安全类型 ## @@ -166,5 +164,7 @@ sign 签名,请求无法进行伪造 - 不安全的反序列化漏洞 ## 参考链接 ## +- https://github.com/CHYbeta/Web-Security-Learning - security-guide-for-developers -[https://github.com/FallibleInc/security-guide-for-developers](https://github.com/FallibleInc/security-guide-for-developers "security-guide-for-developers") \ No newline at end of file +[https://github.com/FallibleInc/security-guide-for-developers](https://github.com/FallibleInc/security-guide-for-developers "security-guide-for-developers") +- https://github.com/qazbnm456/awesome-web-security/blob/master/README-zh.md diff --git a/study-security/pom.xml b/study-security/pom.xml index ed86babb6..df5194a1f 100644 --- a/study-security/pom.xml +++ b/study-security/pom.xml @@ -14,6 +14,8 @@ http://maven.apache.org UTF-8 + 1.8 + 1.8 4.3.9.RELEASE UTF-8 3.1.0 diff --git a/study-sentinel/README.md b/study-sentinel/README.md new file mode 100644 index 000000000..0ba572424 --- /dev/null +++ b/study-sentinel/README.md @@ -0,0 +1,103 @@ + +## study-sentinel +## 链接 + +官网 https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D + +使用 +https://sentinelguard.io/zh-cn/docs/basic-api-resource-rule.html + +如何使用 + +https://github.com/alibaba/Sentinel/wiki/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8 + +## 依赖 + + +com.alibaba.csp +sentinel-core +1.8.4 + + + + +流量控制有以下几个角度: + +- 资源的调用关系,例如资源的调用链路,资源和资源之间的关系; +- 运行指标,例如 QPS、线程池、系统负载等; +- 控制的效果,例如直接限流、冷启动、排队等。 + +Sentinel 的设计理念是让您自由选择控制的角度,并进行灵活组合,从而达到想要的效果。 + + + +demo https://github.com/alibaba/Sentinel/wiki/%E6%96%B0%E6%89%8B%E6%8C%87%E5%8D%97#%E5%85%AC%E7%BD%91-demo + + + +服务端控制台 + +https://github.com/alibaba/Sentinel/wiki/%E6%8E%A7%E5%88%B6%E5%8F%B0#2-%E5%90%AF%E5%8A%A8%E6%8E%A7%E5%88%B6%E5%8F%B0 + +docer 版本 +https://hub.docker.com/r/bladex/sentinel-dashboard + + + +## 流量控制 + + + +流量控制的手段 + +- 直接拒绝 +- 冷启动 +- 匀速器 + + + +根据调用方限流 + +- 调用方限流 +- 链路限流 +- 关联流量控制 + + + +Sentinel 提供以下几种熔断策略 + +- 慢调用比例 (SLOW_REQUEST_RATIO) +- 异常比例 (ERROR_RATIO) +- 异常数 (ERROR_COUNT) + + + +## Sentinel + +熔断基本过程 + +参考链接: + +https://zhuanlan.zhihu.com/p/399531631 + +https://juejin.cn/post/6875577249078935566 + +https://zhuanlan.zhihu.com/p/64786381 + + + + + +限流基本过程? + + + +VS + +Histrix + + +### interview +https://blog.csdn.net/pastxu/article/details/124531980 +https://blog.csdn.net/pastxu/article/details/124531980 + diff --git a/study-sentinel/pom.xml b/study-sentinel/pom.xml new file mode 100644 index 000000000..1e312c089 --- /dev/null +++ b/study-sentinel/pom.xml @@ -0,0 +1,36 @@ + + + + 4.0.0 + + com.bage + study-sentinel + 0.0.1-SNAPSHOT + + study-sentinel + http://www.example.com + + + UTF-8 + 1.7 + 1.7 + + + + + junit + junit + 4.11 + test + + + + com.alibaba.csp + sentinel-core + 1.8.4 + + + + + diff --git a/study-sentinel/src/main/java/com/bage/HelloSentinel.java b/study-sentinel/src/main/java/com/bage/HelloSentinel.java new file mode 100644 index 000000000..68e64bb62 --- /dev/null +++ b/study-sentinel/src/main/java/com/bage/HelloSentinel.java @@ -0,0 +1,53 @@ +package com.bage; + +import com.alibaba.csp.sentinel.Entry; +import com.alibaba.csp.sentinel.SphU; +import com.alibaba.csp.sentinel.annotation.SentinelResource; +import com.alibaba.csp.sentinel.slots.block.BlockException; +import com.alibaba.csp.sentinel.slots.block.RuleConstant; +import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; +import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager; + +import java.util.ArrayList; +import java.util.List; + +/** + * Hello world! + */ +public class HelloSentinel { + public static void main(String[] args) { + // 配置规则. + initFlowRules(); + + int i = 0; + while (i < 100) { + System.out.print(i + ": "); + // 1.5.0 版本开始可以直接利用 try-with-resources 特性 + try (Entry entry = SphU.entry("HelloWorld")) { + // 被保护的逻辑 + System.out.println("hello world"); + } catch (BlockException ex) { + // 处理被流控的逻辑 + System.out.println("blocked!"); + } + try { + Thread.sleep(100L); + } catch (InterruptedException e) { + e.printStackTrace(); + } + i ++ ; + } + } + + private static void initFlowRules() { + List rules = new ArrayList<>(); + FlowRule rule = new FlowRule(); + rule.setResource("HelloWorld"); + rule.setGrade(RuleConstant.FLOW_GRADE_QPS); + // Set limit QPS to 20. + rule.setCount(2); + rules.add(rule); + FlowRuleManager.loadRules(rules); + } + +} diff --git a/study-sentinel/src/main/java/com/bage/HelloSentinelAnna.java b/study-sentinel/src/main/java/com/bage/HelloSentinelAnna.java new file mode 100644 index 000000000..d40f5daa4 --- /dev/null +++ b/study-sentinel/src/main/java/com/bage/HelloSentinelAnna.java @@ -0,0 +1,50 @@ +package com.bage; + +import com.alibaba.csp.sentinel.Entry; +import com.alibaba.csp.sentinel.SphU; +import com.alibaba.csp.sentinel.annotation.SentinelResource; +import com.alibaba.csp.sentinel.slots.block.BlockException; +import com.alibaba.csp.sentinel.slots.block.RuleConstant; +import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; +import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager; + +import java.util.ArrayList; +import java.util.List; + +/** + * Hello world! + */ +public class HelloSentinelAnna { + + // spring boot ?? + public static void main(String[] args) { + // 配置规则. + String hh = new HelloSentinelAnna().getUserById("hh"); + + initFlowRules(); + } + + // 原本的业务方法. + @SentinelResource(blockHandler = "blockHandlerForGetUser") + public String getUserById(String id) { + throw new RuntimeException("getUserById command failed"); + } + + // blockHandler 函数,原方法调用被限流/降级/系统保护的时候调用 + public String blockHandlerForGetUser(String id, BlockException ex) { + return "admin"; + } + + private static void initFlowRules() { + List rules = new ArrayList<>(); + FlowRule rule = new FlowRule(); + rule.setResource("HelloWorld"); + rule.setGrade(RuleConstant.FLOW_GRADE_QPS); +// rule.setGrade(RuleConstant.DEFAULT_WINDOW_INTERVAL_MS); + // Set limit QPS to 20. + rule.setCount(2); + rules.add(rule); + FlowRuleManager.loadRules(rules); + } + +} diff --git a/study-sentinel/src/main/java/com/bage/HelloWorld.java b/study-sentinel/src/main/java/com/bage/HelloWorld.java new file mode 100644 index 000000000..217ae92ff --- /dev/null +++ b/study-sentinel/src/main/java/com/bage/HelloWorld.java @@ -0,0 +1,50 @@ +package com.bage; + +import com.alibaba.csp.sentinel.Entry; +import com.alibaba.csp.sentinel.SphU; +import com.alibaba.csp.sentinel.annotation.SentinelResource; +import com.alibaba.csp.sentinel.slots.block.BlockException; +import com.alibaba.csp.sentinel.slots.block.RuleConstant; +import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; +import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager; + +import java.util.ArrayList; +import java.util.List; + +/** + * Hello world! + */ +public class HelloWorld { + public static void main(String[] args) { + // 配置规则. + initFlowRules(); + + while (true) { + // 1.5.0 版本开始可以直接利用 try-with-resources 特性 + try (Entry entry = SphU.entry("HelloWorld")) { + // 被保护的逻辑 + System.out.println("hello world"); + } catch (BlockException ex) { + // 处理被流控的逻辑 + System.out.println("blocked!"); + } + } + } + + private static void initFlowRules() { + List rules = new ArrayList<>(); + FlowRule rule = new FlowRule(); + rule.setResource("HelloWorld"); + rule.setGrade(RuleConstant.FLOW_GRADE_QPS); + // Set limit QPS to 20. + rule.setCount(20); + rules.add(rule); + FlowRuleManager.loadRules(rules); + } + + @SentinelResource("HelloWorld") + public void helloWorld() { + // 资源中的逻辑 + System.out.println("hello world"); + } +} diff --git a/study-sentinel/src/test/java/com/bage/AppTest.java b/study-sentinel/src/test/java/com/bage/AppTest.java new file mode 100644 index 000000000..9216cfd04 --- /dev/null +++ b/study-sentinel/src/test/java/com/bage/AppTest.java @@ -0,0 +1,20 @@ +package com.bage; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +/** + * Unit test for simple App. + */ +public class AppTest +{ + /** + * Rigorous Test :-) + */ + @Test + public void shouldAnswerWithTrue() + { + assertTrue( true ); + } +} diff --git a/study-servlet/pom.xml b/study-servlet/pom.xml new file mode 100644 index 000000000..9cbf20ed1 --- /dev/null +++ b/study-servlet/pom.xml @@ -0,0 +1,88 @@ + + + + + study + com.bage + 0.0.1-SNAPSHOT + + 4.0.0 + + study-servlet + + study-servlet + http://www.example.com + + + UTF-8 + 1.8 + 1.8 + + + + + junit + junit + 4.11 + test + + + + + javax.servlet + servlet-api + 3.0-alpha-1 + provided + + + + + + + + + + + + maven-clean-plugin + 3.1.0 + + + + maven-resources-plugin + 3.0.2 + + + maven-compiler-plugin + 3.8.0 + + + maven-surefire-plugin + 2.22.1 + + + maven-jar-plugin + 3.0.2 + + + maven-install-plugin + 2.5.2 + + + maven-deploy-plugin + 2.8.2 + + + + maven-site-plugin + 3.7.1 + + + maven-project-info-reports-plugin + 3.0.0 + + + + + diff --git a/study-servlet/src/main/java/com/bage/App.java b/study-servlet/src/main/java/com/bage/App.java new file mode 100644 index 000000000..65b45b532 --- /dev/null +++ b/study-servlet/src/main/java/com/bage/App.java @@ -0,0 +1,13 @@ +package com.bage; + +/** + * Hello world! + * + */ +public class App +{ + public static void main( String[] args ) + { + System.out.println( "Hello World!" ); + } +} diff --git a/study-servlet/src/main/java/com/bage/HelloServlet.java b/study-servlet/src/main/java/com/bage/HelloServlet.java new file mode 100644 index 000000000..bdd283c84 --- /dev/null +++ b/study-servlet/src/main/java/com/bage/HelloServlet.java @@ -0,0 +1,27 @@ +package com.bage; + +import javax.servlet.ServletException; +import javax.servlet.SingleThreadModel; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * SingleThreadModel 支持每个请求单独一个类实现, 不过不推荐 + */ + +public class HelloServlet extends HttpServlet implements SingleThreadModel { + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + + System.out.println("HelloServlet xxxx"); + + } + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + super.doGet(req, resp); + } +} diff --git a/study-servlet/src/test/java/com/bage/AppTest.java b/study-servlet/src/test/java/com/bage/AppTest.java new file mode 100644 index 000000000..9216cfd04 --- /dev/null +++ b/study-servlet/src/test/java/com/bage/AppTest.java @@ -0,0 +1,20 @@ +package com.bage; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +/** + * Unit test for simple App. + */ +public class AppTest +{ + /** + * Rigorous Test :-) + */ + @Test + public void shouldAnswerWithTrue() + { + assertTrue( true ); + } +} diff --git a/study-sftp/pom.xml b/study-sftp/pom.xml index 905257fe8..98b214014 100644 --- a/study-sftp/pom.xml +++ b/study-sftp/pom.xml @@ -28,8 +28,8 @@ UTF-8 - 1.7 - 1.7 + 1.8 + 1.8 diff --git a/study-shiro-spring-boot/pom.xml b/study-shiro-spring-boot/pom.xml index ba91c4892..b3da965da 100644 --- a/study-shiro-spring-boot/pom.xml +++ b/study-shiro-spring-boot/pom.xml @@ -16,8 +16,8 @@ UTF-8 - 1.7 - 1.7 + 1.8 + 1.8 2.0.1.RELEASE 1.8 diff --git a/study-shiro-tutorial/pom.xml b/study-shiro-tutorial/pom.xml index 66cd5834a..51597502f 100644 --- a/study-shiro-tutorial/pom.xml +++ b/study-shiro-tutorial/pom.xml @@ -12,6 +12,8 @@ http://maven.apache.org UTF-8 + 1.8 + 1.8 diff --git a/study-shiro-web-tutorial/pom.xml b/study-shiro-web-tutorial/pom.xml index 79fdc34ab..829bff6cf 100644 --- a/study-shiro-web-tutorial/pom.xml +++ b/study-shiro-web-tutorial/pom.xml @@ -14,6 +14,8 @@ 1.3.2 UTF-8 + 1.8 + 1.8 4.3.9.RELEASE 3.1.0 2.3.4 diff --git a/study-shiro-web/pom.xml b/study-shiro-web/pom.xml index 266c6567d..8cce15c44 100644 --- a/study-shiro-web/pom.xml +++ b/study-shiro-web/pom.xml @@ -16,6 +16,8 @@ 1.3.2 UTF-8 + 1.8 + 1.8 4.3.9.RELEASE 3.1.0 2.3.4 diff --git a/study-socket/pom.xml b/study-socket/pom.xml index 98308e6dc..4de5fb7ef 100644 --- a/study-socket/pom.xml +++ b/study-socket/pom.xml @@ -14,6 +14,8 @@ http://maven.apache.org UTF-8 + 1.8 + 1.8 diff --git a/study-split-db-tb/pom.xml b/study-split-db-tb/pom.xml index 733faf509..bd31aa5de 100644 --- a/study-split-db-tb/pom.xml +++ b/study-split-db-tb/pom.xml @@ -156,7 +156,7 @@ org.projectlombok lombok - 1.16.18 + 1.18.20 provided diff --git a/study-spring-boot-actuator/pom.xml b/study-spring-boot-actuator/pom.xml index 62f303cb6..d129bdd76 100644 --- a/study-spring-boot-actuator/pom.xml +++ b/study-spring-boot-actuator/pom.xml @@ -17,6 +17,8 @@ UTF-8 + 1.8 + 1.8 2.1.7.RELEASE springio diff --git a/study-spring-boot-building/pom.xml b/study-spring-boot-building/pom.xml index c4187ea8d..9b6274156 100644 --- a/study-spring-boot-building/pom.xml +++ b/study-spring-boot-building/pom.xml @@ -17,6 +17,8 @@ UTF-8 + 1.8 + 1.8 2.0.1.RELEASE 1.8 diff --git a/study-spring-boot-dependencies/pom.xml b/study-spring-boot-dependencies/pom.xml index c90827882..972c9d519 100644 --- a/study-spring-boot-dependencies/pom.xml +++ b/study-spring-boot-dependencies/pom.xml @@ -14,6 +14,8 @@ UTF-8 + 1.8 + 1.8 2.0.1.RELEASE diff --git a/study-spring-boot-docker/pom.xml b/study-spring-boot-docker/pom.xml index 79529c8bd..26632bd69 100644 --- a/study-spring-boot-docker/pom.xml +++ b/study-spring-boot-docker/pom.xml @@ -18,6 +18,8 @@ UTF-8 + 1.8 + 1.8 2.0.1.RELEASE springio diff --git a/study-spring-boot-dynamic-datasource/pom.xml b/study-spring-boot-dynamic-datasource/pom.xml index ebc31aa28..6c069dfdd 100644 --- a/study-spring-boot-dynamic-datasource/pom.xml +++ b/study-spring-boot-dynamic-datasource/pom.xml @@ -16,6 +16,8 @@ UTF-8 + 1.8 + 1.8 2.0.1.RELEASE 3.4.6 diff --git a/study-spring-boot-elk/pom.xml b/study-spring-boot-elk/pom.xml new file mode 100644 index 000000000..1f8087af1 --- /dev/null +++ b/study-spring-boot-elk/pom.xml @@ -0,0 +1,84 @@ + + + + 4.0.0 + + com.bage + study-spring-boot-elk + 0.0.1-SNAPSHOT + + study-spring-boot-elk + + http://www.example.com + + + + UTF-8 + 1.8 + 1.8 + 2.0.1.RELEASE + springio + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-log4j2 + + + org.springframework.boot + spring-boot-starter-test + + + + + net.logstash.logback + logstash-logback-encoder + 5.1 + + + + + + + + + + + + org.springframework.boot + spring-boot-dependencies + ${spring.boot.base.version} + pom + import + + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + + + diff --git a/study-spring-boot-elk/src/main/java/com/bage/ElkApplication.java b/study-spring-boot-elk/src/main/java/com/bage/ElkApplication.java new file mode 100644 index 000000000..36b145dc7 --- /dev/null +++ b/study-spring-boot-elk/src/main/java/com/bage/ElkApplication.java @@ -0,0 +1,55 @@ +/* + * Copyright 2013-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.bage; + + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.web.client.RestTemplate; + +import java.time.LocalDateTime; + +/** + * @author lrh + */ +@SpringBootApplication +public class ElkApplication implements CommandLineRunner { + + private static final Logger log = LoggerFactory.getLogger(ElkApplication.class); + + + public static void main(String[] args) { + SpringApplication.run(ElkApplication.class, args); + } + + @Bean + public RestTemplate restTemplate() { + return new RestTemplate(); + } + + @Override + public void run(String... args) throws Exception { + for (int i = 0; i < 1000; i++) { + Thread.sleep(3000L); + log.info("run: {}", LocalDateTime.now().toString()); + } + } +} \ No newline at end of file diff --git a/study-spring-boot-elk/src/main/resources/application.yml b/study-spring-boot-elk/src/main/resources/application.yml new file mode 100644 index 000000000..6cae837b9 --- /dev/null +++ b/study-spring-boot-elk/src/main/resources/application.yml @@ -0,0 +1,3 @@ +spring: + application: + name: hello-hhh diff --git a/study-spring-boot-elk/src/main/resources/logback.xml b/study-spring-boot-elk/src/main/resources/logback.xml new file mode 100644 index 000000000..4122587df --- /dev/null +++ b/study-spring-boot-elk/src/main/resources/logback.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + INFO + + + + ${CONSOLE_LOG_PATTERN} + utf8 + + + + + + 127.0.0.1:8044 + + + + + UTC + + + + { + "severity": "%level", + "appName":"${springAppName:-}", + "service": "${springAppName:-}", + "trace": "%X{X-B3-TraceId:-}", + "span": "%X{X-B3-SpanId:-}", + "exportable": "%X{X-Span-Export:-}", + "pid": "${PID:-}", + "thread": "%thread", + "class": "%logger{40}", + "rest": "%message" + } + + + + + + + + + + + + + \ No newline at end of file diff --git a/study-spring-boot-env/pom.xml b/study-spring-boot-env/pom.xml index b3361bf34..cdea27078 100644 --- a/study-spring-boot-env/pom.xml +++ b/study-spring-boot-env/pom.xml @@ -17,6 +17,8 @@ UTF-8 + 1.8 + 1.8 2.0.1.RELEASE diff --git a/study-spring-boot-jpa/pom.xml b/study-spring-boot-jpa/pom.xml index 5517cc8c3..385be6e5a 100644 --- a/study-spring-boot-jpa/pom.xml +++ b/study-spring-boot-jpa/pom.xml @@ -17,6 +17,8 @@ UTF-8 + 1.8 + 1.8 2.0.1.RELEASE diff --git a/study-spring-boot-metrics/pom.xml b/study-spring-boot-metrics/pom.xml index 8aaa3a1ce..cf59611c5 100644 --- a/study-spring-boot-metrics/pom.xml +++ b/study-spring-boot-metrics/pom.xml @@ -17,6 +17,8 @@ UTF-8 + 1.8 + 1.8 2.0.1.RELEASE diff --git a/study-spring-boot-multi-datasource/pom.xml b/study-spring-boot-multi-datasource/pom.xml index 355aaa4c8..0860bf90e 100644 --- a/study-spring-boot-multi-datasource/pom.xml +++ b/study-spring-boot-multi-datasource/pom.xml @@ -16,6 +16,8 @@ UTF-8 + 1.8 + 1.8 2.0.1.RELEASE 3.4.6 diff --git a/study-spring-boot-oauth2-auth-server/pom.xml b/study-spring-boot-oauth2-auth-server/pom.xml index 5b5a484cb..7084e0659 100644 --- a/study-spring-boot-oauth2-auth-server/pom.xml +++ b/study-spring-boot-oauth2-auth-server/pom.xml @@ -14,7 +14,7 @@ 1.8 1.8 - UTF-8 + 2.0.1.RELEASE diff --git a/study-spring-boot-sso-oauth2-server/readme-sso.md b/study-spring-boot-sso-oauth2-server/readme-sso.md new file mode 100644 index 000000000..c60e916fc --- /dev/null +++ b/study-spring-boot-sso-oauth2-server/readme-sso.md @@ -0,0 +1,384 @@ +# SSO # + + + +## 基本概念 ## + +### 什么是SSO ### +登陆 + +认证 + +授权 + +Single Sign On,单点登录,一次登录,处处【子系统】登录 + + + +### OAuth2.0相关概念 ### + +#### 四个职责划分 #### +- resource owner + + An entity capable of granting access to a protected resource. + When the resource owner is a person, it is referred to as an + end-user. + +- resource server + + The server hosting the protected resources, capable of accepting + and responding to protected resource requests using access tokens. + +- client + + An application making protected resource requests on behalf of the + resource owner and with its authorization. The term "client" does + not imply any particular implementation characteristics (e.g., + whether the application executes on a server, a desktop, or other + devices). + +- authorization server + + The server issuing access tokens to the client after successfully + authenticating the resource owner and obtaining authorization. + +#### 四种模式 #### +- 授权码模式 +老大,666,不会错 +- 密码模式 +传密码,不提倡 +- 简化模式 +不经过client的server端,用户透明,不建议 +- 客户端模式 +client + secret,不细化到用户,不会用 + +## 背景 ## +- 单体应用逐渐不能满足要求 +- 企业内部独立服务众多与统一账号管理存在矛盾 +- 集群以及多系统登录复杂性 + +## 实现方案 ## +### 自实现 ### +- 独立自主完成,全部自己实现一个单点登录 +### CAS ### +Central Authentication Service,耶鲁大学的统一鉴权服务 + +#### 优点 #### +- 开源,可定制化 +- Spring Webflow/Spring Boot Java server component +- 多种认证支持 (LDAP, Database, X.509, SPNEGO, JAAS, JWT, RADIUS, MongoDb, etc) +- 多种协议支持 (CAS, SAML, WS-Federation, OAuth2, OpenID, OpenID Connect, REST) +- 实时监控和跟踪应用程序行为,统计信息和日志。 +- 跨平台的客户端支持 (Java, .Net, PHP, Perl, Apache, etc) + +#### 不足 #### +- 基于cookie +- 客户端管理问题? + +#### 核心 #### +- CAS Server + TicketGrantingTicketCheckAction 校验ticket + AcceptUsersAuthenticationHandler 校验用户登录 +- CAS Clients + AuthenticationFilter 核心入口,校验登录 + 重定向等 + AbstractTicketValidationFilter(AuthenticationFilter调用) 校验ticket + CasHttpServletRequestWrapper + +#### 认证流程 #### +![Web flow diagram](https://apereo.github.io/cas/4.2.x/images/cas_flow_diagram.png) + +### Spring OAuth2.0 ### +Spring 的,基于 OAuth2.0 协议的, SSO实现方案 + +#### 优点 #### +- Spring 全家桶之一,与Spring配合默契,喜欢的样子它都有 +- 客户端统一管理 零成本 + +#### 不足 #### +- OAuth2.0 的Java 应用客户端 + +#### 核心 #### +- Spring Security + 实现 *AuthenticationProvider* 用户登录验证 + 实现 *UserDetailsService* 用户loadUserByUsername + 集成 *WebSecurityConfigurerAdapter* 装配 自定义用户登录验证 + URL Pattern +- Spring 集成 OAuth2.0 + *AuthorizationEndpoint* 认证登录 + *TokenEndpoint* 验证Token + *OAuth2AuthenticationProcessingFilter* is used to load the Authentication for the request given an authenticated access token. + + *ClientDetailsServiceConfigurer* 客户端信息配置 + *AuthorizationServerSecurityConfigurer*: defines the security constraints on the token endpoint. + *AuthorizationServerEndpointsConfigurer*: defines the authorization and token endpoints and the token services. + +#### 认证流程 #### +授权码模式 + + +--------+ +---------------+ + | |--(A)- Authorization Request ->| Resource | + | | | Owner | + | |<-(B)-- Authorization Grant ---| | + | | +---------------+ + | | + | | +---------------+ + | |--(C)-- Authorization Grant -->| Authorization | + | Client | | Server | + | |<-(D)----- Access Token -------| | + | | +---------------+ + | | + | | +---------------+ + | |--(E)----- Access Token ------>| Resource | + | | | Server | + | |<-(F)--- Protected Resource ---| | + +--------+ +---------------+ + + Figure 1: Abstract Protocol Flow + + 用户访问受保护资源,客户端验证用户登录状态,未登录,则进入步骤A + + (A) 客户端请求用户给予授权,页面跳转到登录页面,用户输入用户名、密码进行登录,登录成功后,则进入步骤B + + (B) 授权后,返回一个code + state(可选),进入步骤C + + (C) 客户端拿到授权码,凭借授权码code请求授权中心验证,进入步骤D + + (D) 若验证成功,则返回AccessToken.则进入步骤E + + (E) 客户端拿到AccessToken,带上,请求受保护资源. + + (F) 资源服务器验证token合法,则返回受保护的资源. + + + +### Spring OAuth2.0 SSO ### + +服务端-拦截规则 + +```java + @Override +protected void configure(HttpSecurity http) throws Exception { + http.authorizeRequests() + .antMatchers("/oauth/**","/login/**", "/logout").permitAll() + .anyRequest().authenticated() // 其他地址的访问均需验证权限 + .and() + .formLogin() + .loginPage("/login") + .and() + .logout().logoutSuccessUrl("/"); +} +``` +服务端-资源路径 + + @Override + public void configure(HttpSecurity http) throws Exception { + http.requestMatchers().antMatchers("/user/**") + .and() + .authorizeRequests() + .anyRequest().authenticated(); + } +服务端-客户端列表 + +```java +@Override +public void configure(ClientDetailsServiceConfigurer clients) throws Exception { + clients.jdbc(dataSource); + /* + clients.inMemory() + .withClient("sampleClientId") + .secret(passwordEncoder.encode("secret")) + .authorizedGrantTypes("authorization_code") + .scopes("user_info") + .autoApprove(true) + .redirectUris("http://localhost:8001/client1/login") + // .accessTokenValiditySeconds(3600) + ; // 1 hour +*/ +} +``` + +SQL + +```sql +INSERT INTO oauth_client_details + (client_id, client_secret, scope, authorized_grant_types, + web_server_redirect_uri, authorities, access_token_validity, + refresh_token_validity, additional_information, autoapprove) +VALUES + ('sampleClientId', '$2a$10$i7bPh8Npg8lsUTxOAwp7suAwMxTw8tNyRkvDJT8/uZGcSzdFocHS6', 'user_info', + 'password,authorization_code,refresh_token,client_credentials', 'http://localhost:8001/client1/login,http://localhost:8002/client2/login', null, 36000, 36000, null, true); +``` +客户端- application.yml + +```yml +server: + port: 8001 + servlet: + context-path: /client1 + +security: + oauth2: + client: + client-id: sampleClientId + client-secret: secret + access-token-uri: http://localhost:8080/oauth/token + user-authorization-uri: http://localhost:8080/oauth/authorize + resource: + user-info-uri: http://localhost:8080/user/me +``` + 客户端-拦截规则 + +```java +@EnableOAuth2Sso +@Configuration +@EnableGlobalMethodSecurity(prePostEnabled = true) +public class ClientSecurityConfig extends WebSecurityConfigurerAdapter { + @Override + public void configure(HttpSecurity http) throws Exception { + http.antMatcher("/**") + .authorizeRequests() + .antMatchers("/", "/login**", "/webjars/**", "/error**").permitAll() + .anyRequest() + .authenticated() + .and().logout().logoutSuccessUrl("/") + .and().csrf() + .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()); + ; + } +} +``` +完整代码 + + +- Auth Server [https://github.com/bage2014/study/tree/master/study-spring-boot-sso-oauth2-server](https://github.com/bage2014/study/tree/master/study-spring-boot-sso-oauth2-server) +- Client [https://github.com/bage2014/study/tree/master/study-spring-boot-sso-oauth2-client1](https://github.com/bage2014/study/tree/master/study-spring-boot-sso-oauth2-client1) + +Ctrip SSO [http://git.ctripcorp.com/global-rail-gds/gds-admin-application](http://git.ctripcorp.com/global-rail-gds/gds-admin-application) + +### JWT ### +核心实现 + +1. 登录过程 + + public String login(HttpServletRequest request){ + + String account = request.getParameter("account"); + Map map = new HashMap(); + map.put("account", account); + map.put("password", request.getParameter("password")); + map = userService.query(map); + + if(map != null ) { //登录成功 + + Key key = MacProvider.generateKey(); + Map cliams = new HashMap(); + cliams.put("jti", String.valueOf(System.currentTimeMillis())); + String jws = Jwts.builder() + .setClaims(cliams ) + .setIssuer("com.bage") // iss: 该JWT的签发者,是否使用是可选的 + .setSubject(account) // sub: 该JWT所面向的用户,是否使用是可选的; + //.setAudience(aud) // aud: 接收该JWT的一方,是否使用是可选的 + .setExpiration(DateUtils.getJwtsExpirationDate()) // exp(expires): 什么时候过期,这里是一个Unix时间戳,是否使用是可选的 + .setIssuedAt(DateUtils.now()) // iat(issued at): 在什么时候签发的(UNIX时间),是否使用是可选的 + //.setNotBefore(DateUtils.getNotBeforeDate()) // nbf (Not Before):如果当前时间在nbf里的时间之前,则Token不被接受;一般都会留一些余地,比如几分钟;是否使用是可选的 + .signWith(SignatureAlgorithm.HS512, key) + .compact(); + + System.out.println("jws:" + jws); + + RedisUtils.put(Constants.redis_key_jwt + "_" + account, jws); + RedisUtils.put(Constants.redis_key_jwtkey + "_" + account, key); + + map = new HashMap(); + map.put("jws", jws); + map.put("now", DateUtils.now()); + map.put("expirationDate", DateUtils.getJwtsExpirationDate()); + + return JsonUtils.toJson(map); + + } else { // 登录失败 + return ""; + } + } +2. 校验逻辑 + +```java +if(!isExcludeUri(request)) { + String claimsJws = request.getHeader("Authorization"); + System.out.println("参数compactJws:" + claimsJws); + // 签名验证 + try { + Claims claims = JwtsBuilder.decodeTokenClaims(claimsJws); + String subject = claims.getSubject(); + Key key = (Key) RedisUtils.get(Constants.redis_key_jwt + "_" + subject); + Jws jws = Jwts.parser().setSigningKey(key).parseClaimsJws(claimsJws); + System.out.println("jti:" + jws.getBody().get("jti")); + String currentCompactJws = RedisUtils.getString(Constants.redis_key_jwt + "_" + subject); + if(currentCompactJws == null || !currentCompactJws.equals(claimsJws)) { + System.out.println("签名不合法:\ncompactJws:" + claimsJws); + System.out.println("currentCompactJws:" + currentCompactJws); + checkJwtFail(request, response); + return ; + } + } catch (Exception e) { + e.printStackTrace(); + System.out.println("解析签名报错"); + checkJwtFail(request, response); + return ; + } + } + + chain.doFilter(request, response); +``` + +3. 区分请求类型 + +```java +private void checkJwtFail(HttpServletRequest request,HttpServletResponse response) { + try { + String requestType = request.getHeader("X-Requested-With"); + if("XMLHttpRequest".equals(requestType)){ // TODO 增加跨域请求校验 + System.out.println("AJAX请求.."); + response.getWriter().print("Authorization Failed"); + }else{ + System.out.println("非AJAX请求.."); + response.sendRedirect(request.getServletContext().getContextPath() + "/"); + //此时requestType为null + } + //request.getSession().removeAttribute(Constants.session_attribute_currentuser); + } catch (IOException e) { + e.printStackTrace(); + } +} +``` + +完整代码 + +单体应用 [https://github.com/bage2014/study/tree/master/study-jwt](https://github.com/bage2014/study/tree/master/study-jwt) + +## 总结 ## + +### SSO的本质 ### +- 共享登录信息(共用Session || JWT) +- 认证中心 + 客户端模式 + +### oauth2.0 与 SSO 的关系 ### +- Oauth2.0 是 一种协议,SSO是 一个功能 +- SSO可以基于 OAuth 2.0协议实现 +- Oauth2.0可以用于其他功能,比如资源授权 +### SSO实现方案的抉择 ### +- 合适的就是最好的 +- 企业之间 || Restful,推荐 Spring OAuth2.0 +### Spring OAuth2.0 JWT ### +- Session 弱化场景 +- 防重放 +- JWT失效时间控制 + +## 参考链接 ## +- 单点登录 CAS 官网 [https://www.apereo.org/projects/cas](https://www.apereo.org/projects/cas) +- cas 文档 [https://apereo.github.io/cas/4.2.x/index.html](https://apereo.github.io/cas/4.2.x/index.html) +- cas github地址 [https://github.com/apereo/cas](https://github.com/apereo/cas) +- CAS 登录过程源码解析 [https://segmentfault.com/a/1190000014001205?utm_source=channel-hottest](https://segmentfault.com/a/1190000014001205?utm_source=channel-hottest) +- Oauth2.0协议 [http://www.rfcreader.com/#rfc6749](http://www.rfcreader.com/#rfc6749) +- 阮一峰 理解OAuth 2.0 [http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html](http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html) +- Spring OAuth 2.0 官网 [https://projects.spring.io/spring-security-oauth/docs/oauth2.html](https://projects.spring.io/spring-security-oauth/docs/oauth2.html) +- 携程单点登录接入Conf [http://conf.ctripcorp.com/pages/viewpage.action?pageId=157466083](http://conf.ctripcorp.com/pages/viewpage.action?pageId=157466083)、[http://conf.ctripcorp.com/pages/viewpage.action?pageId=135658955](http://conf.ctripcorp.com/pages/viewpage.action?pageId=135658955) diff --git a/study-spring-boot-test/README.md b/study-spring-boot-test/README.md new file mode 100644 index 000000000..7fef6fb70 --- /dev/null +++ b/study-spring-boot-test/README.md @@ -0,0 +1,6 @@ + + + +## Spring Test +https://docs.spring.io/spring-framework/docs/current/reference/html/testing.html#testing + diff --git a/study-spring-boot-trace-server/README.md b/study-spring-boot-trace-server/README.md new file mode 100644 index 000000000..35766832e --- /dev/null +++ b/study-spring-boot-trace-server/README.md @@ -0,0 +1,7 @@ +# study-spring-boot-trace-server + + +## 参考链接 +https://spring.io/guides/tutorials/metrics-and-tracing/ + + diff --git a/study-spring-boot-trace-server/pom.xml b/study-spring-boot-trace-server/pom.xml new file mode 100644 index 000000000..7cb1be1ce --- /dev/null +++ b/study-spring-boot-trace-server/pom.xml @@ -0,0 +1,107 @@ + + + + 4.0.0 + + com.bage + study-spring-boot-trace-server + 0.0.1-SNAPSHOT + + study-spring-boot-trace-server + http://www.example.com + + + UTF-8 + 1.8 + 1.8 + + 2.0.1.RELEASE + Finchley.SR2 + + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-log4j2 + + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + org.springframework.boot + spring-boot-starter-test + + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-starter-webflux + + + + + + + org.springframework.cloud + spring-cloud-starter-sleuth + + + + org.projectlombok + lombok + 1.18.20 + true + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + + + org.springframework.boot + spring-boot-dependencies + ${spring.boot.base.version} + pom + import + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring.cloud.version} + pom + import + + + + + + diff --git a/study-spring-boot-trace-server/src/main/java/com/bage/AvailabilityController.java b/study-spring-boot-trace-server/src/main/java/com/bage/AvailabilityController.java new file mode 100644 index 000000000..434a3e797 --- /dev/null +++ b/study-spring-boot-trace-server/src/main/java/com/bage/AvailabilityController.java @@ -0,0 +1,44 @@ +package com.bage; + +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; + +import java.util.*; + +@RestController +public class AvailabilityController { + + private boolean validate(String console) { + Set set = new HashSet(); + set.addAll(Arrays.asList("ps5", "ps4", "switch", "xbox")); + return StringUtils.hasText(console) && + set.contains(console); + } + + @GetMapping("/availability/{console}") + Map getAvailability(@PathVariable String console) { + Map map = new HashMap(); + map.put("console", console); + map.put("available", checkAvailability(console)); + return map; + } + + private boolean checkAvailability(String console) { + Assert.state(validate(console), () -> "the console specified, " + console + ", is not valid."); + boolean result = false; + switch (console) { + case "ps5": + throw new RuntimeException("Service exception"); + case "xbox": + result = true; + break; + default: + result = false; + break; + } + return result; + } +} \ No newline at end of file diff --git a/study-spring-boot-trace-server/src/main/java/com/bage/ServiceApplication.java b/study-spring-boot-trace-server/src/main/java/com/bage/ServiceApplication.java new file mode 100644 index 000000000..c749ea851 --- /dev/null +++ b/study-spring-boot-trace-server/src/main/java/com/bage/ServiceApplication.java @@ -0,0 +1,15 @@ +package com.bage; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@Slf4j +@SpringBootApplication +public class ServiceApplication { + + public static void main(String[] args) { + log.info("starting server"); + SpringApplication.run(ServiceApplication.class, args); + } +} diff --git a/study-spring-boot-trace-server/src/main/resources/application.properties b/study-spring-boot-trace-server/src/main/resources/application.properties new file mode 100644 index 000000000..c5e12099a --- /dev/null +++ b/study-spring-boot-trace-server/src/main/resources/application.properties @@ -0,0 +1,5 @@ +spring.application.name=service +server.port=8083 +wavefront.application.name=console-availability +management.metrics.export.wavefront.source=my-cloud-server + diff --git a/study-spring-boot-trace-server/src/test/java/com/bage/AppTest.java b/study-spring-boot-trace-server/src/test/java/com/bage/AppTest.java new file mode 100644 index 000000000..9216cfd04 --- /dev/null +++ b/study-spring-boot-trace-server/src/test/java/com/bage/AppTest.java @@ -0,0 +1,20 @@ +package com.bage; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +/** + * Unit test for simple App. + */ +public class AppTest +{ + /** + * Rigorous Test :-) + */ + @Test + public void shouldAnswerWithTrue() + { + assertTrue( true ); + } +} diff --git a/study-spring-boot-trace/README.md b/study-spring-boot-trace/README.md new file mode 100644 index 000000000..421052b5c --- /dev/null +++ b/study-spring-boot-trace/README.md @@ -0,0 +1,7 @@ +# study-spring-boot-trace + + +## 参考链接 +https://spring.io/guides/tutorials/metrics-and-tracing/ + + diff --git a/study-spring-boot-trace/pom.xml b/study-spring-boot-trace/pom.xml new file mode 100644 index 000000000..ae21ec695 --- /dev/null +++ b/study-spring-boot-trace/pom.xml @@ -0,0 +1,77 @@ + + + + 4.0.0 + + com.bage + study-spring-boot-trace + 0.0.1-SNAPSHOT + + study-spring-boot-trace + http://www.example.com + + + UTF-8 + 1.8 + 1.8 + + 2.0.1.RELEASE + Finchley.SR2 + + + + + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-starter-webflux + + + org.springframework.cloud + spring-cloud-starter-sleuth + + + + org.projectlombok + lombok + 1.18.20 + true + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + + + + org.springframework.boot + spring-boot-dependencies + ${spring.boot.base.version} + pom + import + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring.cloud.version} + pom + import + + + + + + + diff --git a/study-spring-boot-trace/src/main/java/com/bage/Availability.java b/study-spring-boot-trace/src/main/java/com/bage/Availability.java new file mode 100644 index 000000000..c9c7c147c --- /dev/null +++ b/study-spring-boot-trace/src/main/java/com/bage/Availability.java @@ -0,0 +1,13 @@ +package com.bage; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +class Availability { + private boolean available; + private String console; +} \ No newline at end of file diff --git a/study-spring-boot-trace/src/main/java/com/bage/AvailabilityClient.java b/study-spring-boot-trace/src/main/java/com/bage/AvailabilityClient.java new file mode 100644 index 000000000..ef339d170 --- /dev/null +++ b/study-spring-boot-trace/src/main/java/com/bage/AvailabilityClient.java @@ -0,0 +1,24 @@ +package com.bage; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Mono; + +@Component +@RequiredArgsConstructor +public class AvailabilityClient { + + private final WebClient webClient; + private static final String URI = "http://localhost:8083/availability/{console}"; + + Mono checkAvailability(String console) { + return this.webClient + .get() + .uri(URI, console) + .retrieve() + .bodyToMono(Availability.class) + .onErrorReturn(new Availability(false, console)); + } + +} \ No newline at end of file diff --git a/study-spring-boot-trace/src/main/java/com/bage/ClientApplication.java b/study-spring-boot-trace/src/main/java/com/bage/ClientApplication.java new file mode 100644 index 000000000..bb425937c --- /dev/null +++ b/study-spring-boot-trace/src/main/java/com/bage/ClientApplication.java @@ -0,0 +1,40 @@ +package com.bage; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.ApplicationListener; +import org.springframework.context.annotation.Bean; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Flux; + +import java.time.Duration; + +@Slf4j +@SpringBootApplication +public class ClientApplication { + + public static void main(String[] args) { + log.info("starting client"); + SpringApplication.run(ClientApplication.class, args); + } + + @Bean + WebClient webClient(WebClient.Builder builder) { + return builder.build(); + } + + @Bean + ApplicationListener ready(AvailabilityClient client) { + return applicationReadyEvent -> { + for (String console : "ps5,xbox,ps4,switch".split(",")) { + Flux.range(0, 20).delayElements(Duration.ofMillis(100)).subscribe(i -> + client + .checkAvailability(console) + .subscribe(availability -> + log.info("console: {}, availability: {} ", console, availability.isAvailable()))); + } + }; + } +} diff --git a/study-spring-boot-trace/src/main/resources/application.properties b/study-spring-boot-trace/src/main/resources/application.properties new file mode 100644 index 000000000..f584da824 --- /dev/null +++ b/study-spring-boot-trace/src/main/resources/application.properties @@ -0,0 +1,4 @@ + +spring.application.name=client +wavefront.application.name=console-availability +management.metrics.export.wavefront.source=my-cloud-server diff --git a/study-spring-boot-trace/src/test/java/com/bage/AppTest.java b/study-spring-boot-trace/src/test/java/com/bage/AppTest.java new file mode 100644 index 000000000..9216cfd04 --- /dev/null +++ b/study-spring-boot-trace/src/test/java/com/bage/AppTest.java @@ -0,0 +1,20 @@ +package com.bage; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +/** + * Unit test for simple App. + */ +public class AppTest +{ + /** + * Rigorous Test :-) + */ + @Test + public void shouldAnswerWithTrue() + { + assertTrue( true ); + } +} diff --git a/study-spring-boot-valid/README.md b/study-spring-boot-valid/README.md new file mode 100644 index 000000000..425f65f3a --- /dev/null +++ b/study-spring-boot-valid/README.md @@ -0,0 +1,9 @@ +# study-spring-boot-valid + +## 参考链接 +todo : 待确认,,,, +https://spring.io/guides/gs/validating-form-input/ + +https://cloud.tencent.com/developer/article/1193178?from=15425 + + diff --git a/study-spring-boot-valid/pom.xml b/study-spring-boot-valid/pom.xml new file mode 100644 index 000000000..cbb92b226 --- /dev/null +++ b/study-spring-boot-valid/pom.xml @@ -0,0 +1,86 @@ + + + + + study + com.bage + 0.0.1-SNAPSHOT + + 4.0.0 + + study-spring-boot-valid + + study-spring-boot-valid + http://www.example.com + + + UTF-8 + 1.8 + 1.8 + + + 2.0.1.RELEASE + + + + + + org.springframework.boot + spring-boot-starter-validation + 2.0.0.RELEASE + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-log4j2 + + + org.springframework.boot + spring-boot-starter-test + + + + org.springframework.boot + spring-boot-starter-actuator + + + + org.projectlombok + lombok + 1.18.20 + true + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + + org.springframework.boot + spring-boot-dependencies + ${spring.boot.base.version} + pom + import + + + + + + diff --git a/study-spring-boot-valid/src/main/java/com/bage/spring/boot/valid/PersonForm.java b/study-spring-boot-valid/src/main/java/com/bage/spring/boot/valid/PersonForm.java new file mode 100644 index 000000000..9b7b3c958 --- /dev/null +++ b/study-spring-boot-valid/src/main/java/com/bage/spring/boot/valid/PersonForm.java @@ -0,0 +1,36 @@ +package com.bage.spring.boot.valid; + +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +public class PersonForm { + + @NotNull + @Size(min=2, max=30) + private String name; + + @NotNull + @Min(18) + private Integer age; + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + public Integer getAge() { + return age; + } + + public void setAge(Integer age) { + this.age = age; + } + + public String toString() { + return "Person(Name: " + this.name + ", Age: " + this.age + ")"; + } +} \ No newline at end of file diff --git a/study-spring-boot-valid/src/main/java/com/bage/spring/boot/valid/ValidLogic.java b/study-spring-boot-valid/src/main/java/com/bage/spring/boot/valid/ValidLogic.java new file mode 100644 index 000000000..39e518284 --- /dev/null +++ b/study-spring-boot-valid/src/main/java/com/bage/spring/boot/valid/ValidLogic.java @@ -0,0 +1,33 @@ +package com.bage.spring.boot.valid; + +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Controller; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import javax.validation.*; +import java.util.Set; + + +@Component +public class ValidLogic { + + public String hello(PersonForm personForm) { + //引入校验工具 + ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); + //获取校验器 + Validator validator = factory.getValidator(); + //执行校验 + Set> violationSet = validator.validate(personForm); + violationSet.forEach(violat -> { + System.out.println(violat.getPropertyPath());//校验错误的域 + System.out.println(violat.getMessage());//校验错误的信息 + }); + return "form"; + } + +} \ No newline at end of file diff --git a/study-spring-boot-valid/src/main/java/com/bage/spring/boot/valid/ValidatingFormInputApplication.java b/study-spring-boot-valid/src/main/java/com/bage/spring/boot/valid/ValidatingFormInputApplication.java new file mode 100644 index 000000000..8b5fcc9af --- /dev/null +++ b/study-spring-boot-valid/src/main/java/com/bage/spring/boot/valid/ValidatingFormInputApplication.java @@ -0,0 +1,13 @@ +package com.bage.spring.boot.valid; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class ValidatingFormInputApplication { + + public static void main(String[] args) throws Exception { + SpringApplication.run(ValidatingFormInputApplication.class, args); + } + +} \ No newline at end of file diff --git a/study-spring-boot-valid/src/main/java/com/bage/spring/boot/valid/WebController.java b/study-spring-boot-valid/src/main/java/com/bage/spring/boot/valid/WebController.java new file mode 100644 index 000000000..d623a8364 --- /dev/null +++ b/study-spring-boot-valid/src/main/java/com/bage/spring/boot/valid/WebController.java @@ -0,0 +1,45 @@ +package com.bage.spring.boot.valid; + +import javax.validation.Valid; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + + +@Controller +public class WebController implements WebMvcConfigurer { + + @Autowired + private ValidLogic validLogic; + + @Override + public void addViewControllers(ViewControllerRegistry registry) { + registry.addViewController("/results").setViewName("results"); + } + + @GetMapping("/hello") + public String hello() { + return validLogic.hello(new PersonForm()); + } + + @GetMapping("/") + public String showForm(PersonForm personForm) { + return "form"; + } + + @PostMapping("/") + public String checkPersonInfo(@Valid PersonForm personForm, BindingResult bindingResult) { + + if (bindingResult.hasErrors()) { + return "form"; + } + + return "redirect:/results"; + } +} \ No newline at end of file diff --git a/study-spring-boot-valid/src/main/resources/templates/form.html b/study-spring-boot-valid/src/main/resources/templates/form.html new file mode 100644 index 000000000..d1bedf294 --- /dev/null +++ b/study-spring-boot-valid/src/main/resources/templates/form.html @@ -0,0 +1,22 @@ + + + +
+ + + + + + + + + + + + + + +
Name:Name Error
Age:Age Error
+
+ + \ No newline at end of file diff --git a/study-spring-boot-valid/src/main/resources/templates/results.html b/study-spring-boot-valid/src/main/resources/templates/results.html new file mode 100644 index 000000000..b881ac31e --- /dev/null +++ b/study-spring-boot-valid/src/main/resources/templates/results.html @@ -0,0 +1,5 @@ + + + Congratulations! You are old enough to sign up for this site. + + \ No newline at end of file diff --git a/study-spring-boot-wavefront/README.md b/study-spring-boot-wavefront/README.md new file mode 100644 index 000000000..0233520d8 --- /dev/null +++ b/study-spring-boot-wavefront/README.md @@ -0,0 +1,8 @@ +# study- spring-boot-warefront + +## 参考链接 + +https://docs.wavefront.com/wavefront_springboot.html + +https://blogs.vmware.com/china/2018/06/04/%E4%BA%91%E7%AB%AF%E7%9B%91%E6%8E%A7%E5%92%8C%E5%88%86%E6%9E%90%E5%B9%B3%E5%8F%B0-wavefront/ + diff --git a/study-spring-boot-wavefront/pom.xml b/study-spring-boot-wavefront/pom.xml new file mode 100644 index 000000000..546f73d51 --- /dev/null +++ b/study-spring-boot-wavefront/pom.xml @@ -0,0 +1,99 @@ + + + + 4.0.0 + + com.bage + study-spring-boot-wavefront + 0.0.1-SNAPSHOT + + study-spring-boot-wavefront + http://www.example.com + + + UTF-8 + 1.8 + 1.8 + + 2.5.3 + 2.2.0 + + + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-log4j2 + + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + org.springframework.boot + spring-boot-starter-test + + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-starter-webflux + + + com.wavefront + wavefront-spring-boot-starter + ${ware.front.version} + + + + org.projectlombok + lombok + 1.18.20 + true + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + + + org.springframework.boot + spring-boot-dependencies + ${spring.boot.base.version} + pom + import + + + + + + + + diff --git a/study-spring-boot-wavefront/src/main/java/com/bage/AvailabilityController.java b/study-spring-boot-wavefront/src/main/java/com/bage/AvailabilityController.java new file mode 100644 index 000000000..add1e9f49 --- /dev/null +++ b/study-spring-boot-wavefront/src/main/java/com/bage/AvailabilityController.java @@ -0,0 +1,21 @@ +package com.bage; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; + +import java.util.HashMap; +import java.util.Map; + +@RestController +public class AvailabilityController { + + @GetMapping("/availability/{console}") + Map getAvailability(@PathVariable String console) { + Map map = new HashMap(); + map.put("console", console); + map.put("available", "true"); + return map; + } + +} \ No newline at end of file diff --git a/study-spring-boot-wavefront/src/main/java/com/bage/ServiceApplication.java b/study-spring-boot-wavefront/src/main/java/com/bage/ServiceApplication.java new file mode 100644 index 000000000..c749ea851 --- /dev/null +++ b/study-spring-boot-wavefront/src/main/java/com/bage/ServiceApplication.java @@ -0,0 +1,15 @@ +package com.bage; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@Slf4j +@SpringBootApplication +public class ServiceApplication { + + public static void main(String[] args) { + log.info("starting server"); + SpringApplication.run(ServiceApplication.class, args); + } +} diff --git a/study-spring-boot-wavefront/src/main/resources/application.properties b/study-spring-boot-wavefront/src/main/resources/application.properties new file mode 100644 index 000000000..12f8d1508 --- /dev/null +++ b/study-spring-boot-wavefront/src/main/resources/application.properties @@ -0,0 +1,4 @@ +server.port=8080 +wavefront.application.name=console-availability +management.metrics.export.wavefront.source=my-cloud-server + diff --git a/study-spring-boot-web-socket/README.md b/study-spring-boot-web-socket/README.md new file mode 100644 index 000000000..f4e6c285c --- /dev/null +++ b/study-spring-boot-web-socket/README.md @@ -0,0 +1,6 @@ +# study-spring-boot-web-socket + +## 参考链接 +https://spring.io/guides/gs/messaging-stomp-websocket/ + + diff --git a/study-spring-boot-web-socket/pom.xml b/study-spring-boot-web-socket/pom.xml new file mode 100644 index 000000000..1b17e3023 --- /dev/null +++ b/study-spring-boot-web-socket/pom.xml @@ -0,0 +1,115 @@ + + + + 4.0.0 + + com.bage + study-spring-boot-web-socket + 0.0.1-SNAPSHOT + + study-spring-boot-web-socket + http://www.example.com + + + + UTF-8 + 1.8 + 1.8 + + 2.0.1.RELEASE + + + + + + org.springframework.boot + spring-boot-starter-validation + 2.0.0.RELEASE + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-log4j2 + + + org.springframework.boot + spring-boot-starter-test + + + + org.springframework.boot + spring-boot-starter-actuator + + + + org.projectlombok + lombok + 1.18.20 + true + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.springframework.boot + spring-boot-starter-websocket + + + org.webjars + webjars-locator-core + + + org.webjars + sockjs-client + 1.0.2 + + + org.webjars + stomp-websocket + 2.3.3 + + + org.webjars + bootstrap + 3.3.7 + + + org.webjars + jquery + 3.1.1-1 + + + + + + + + + + org.springframework.boot + spring-boot-dependencies + ${spring.boot.base.version} + pom + import + + + + + + + diff --git a/study-spring-boot-web-socket/src/main/java/com/bage/Greeting.java b/study-spring-boot-web-socket/src/main/java/com/bage/Greeting.java new file mode 100644 index 000000000..2f14837c4 --- /dev/null +++ b/study-spring-boot-web-socket/src/main/java/com/bage/Greeting.java @@ -0,0 +1,18 @@ +package com.bage; + +public class Greeting { + + private String content; + + public Greeting() { + } + + public Greeting(String content) { + this.content = content; + } + + public String getContent() { + return content; + } + +} \ No newline at end of file diff --git a/study-spring-boot-web-socket/src/main/java/com/bage/GreetingController.java b/study-spring-boot-web-socket/src/main/java/com/bage/GreetingController.java new file mode 100644 index 000000000..113f318c6 --- /dev/null +++ b/study-spring-boot-web-socket/src/main/java/com/bage/GreetingController.java @@ -0,0 +1,25 @@ +package com.bage; + +import org.springframework.messaging.handler.annotation.MessageMapping; +import org.springframework.messaging.handler.annotation.SendTo; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.util.HtmlUtils; + +@Controller +public class GreetingController { + + + @MessageMapping("/hello") + @SendTo("/topic/greetings") + public Greeting greeting(HelloMessage message) throws Exception { + Thread.sleep(1000); // simulated delay + return new Greeting("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!"); + } + +// @RequestMapping("/") +// public String greeting() throws Exception { +// return "/index.html"; +// } + +} \ No newline at end of file diff --git a/study-spring-boot-web-socket/src/main/java/com/bage/HelloMessage.java b/study-spring-boot-web-socket/src/main/java/com/bage/HelloMessage.java new file mode 100644 index 000000000..dec5e2598 --- /dev/null +++ b/study-spring-boot-web-socket/src/main/java/com/bage/HelloMessage.java @@ -0,0 +1,21 @@ +package com.bage; + +public class HelloMessage { + + private String name; + + public HelloMessage() { + } + + public HelloMessage(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} \ No newline at end of file diff --git a/study-spring-boot-web-socket/src/main/java/com/bage/MessagingStompWebsocketApplication.java b/study-spring-boot-web-socket/src/main/java/com/bage/MessagingStompWebsocketApplication.java new file mode 100644 index 000000000..6af0a0e6d --- /dev/null +++ b/study-spring-boot-web-socket/src/main/java/com/bage/MessagingStompWebsocketApplication.java @@ -0,0 +1,12 @@ +package com.bage; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class MessagingStompWebsocketApplication { + + public static void main(String[] args) { + SpringApplication.run(MessagingStompWebsocketApplication.class, args); + } +} \ No newline at end of file diff --git a/study-spring-boot-web-socket/src/main/java/com/bage/WebSocketConfig.java b/study-spring-boot-web-socket/src/main/java/com/bage/WebSocketConfig.java new file mode 100644 index 000000000..10559180d --- /dev/null +++ b/study-spring-boot-web-socket/src/main/java/com/bage/WebSocketConfig.java @@ -0,0 +1,24 @@ +package com.bage; + +import org.springframework.context.annotation.Configuration; +import org.springframework.messaging.simp.config.MessageBrokerRegistry; +import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; +import org.springframework.web.socket.config.annotation.StompEndpointRegistry; +import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; + +@Configuration +@EnableWebSocketMessageBroker +public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { + + @Override + public void configureMessageBroker(MessageBrokerRegistry config) { + config.enableSimpleBroker("/topic"); + config.setApplicationDestinationPrefixes("/app"); + } + + @Override + public void registerStompEndpoints(StompEndpointRegistry registry) { + registry.addEndpoint("/app").withSockJS(); + } + +} \ No newline at end of file diff --git a/study-spring-boot-web-socket/src/main/resources/application.yml b/study-spring-boot-web-socket/src/main/resources/application.yml new file mode 100644 index 000000000..55ee11269 --- /dev/null +++ b/study-spring-boot-web-socket/src/main/resources/application.yml @@ -0,0 +1,6 @@ + + +server: + port: 8080 + servlet: + context-path: /app \ No newline at end of file diff --git a/study-spring-boot-web-socket/src/main/resources/static/app.js b/study-spring-boot-web-socket/src/main/resources/static/app.js new file mode 100644 index 000000000..73195e52e --- /dev/null +++ b/study-spring-boot-web-socket/src/main/resources/static/app.js @@ -0,0 +1,50 @@ +var stompClient = null; + +function setConnected(connected) { + $("#connect").prop("disabled", connected); + $("#disconnect").prop("disabled", !connected); + if (connected) { + $("#conversation").show(); + } + else { + $("#conversation").hide(); + } + $("#greetings").html(""); +} + +function connect() { + var socket = new SockJS('/app'); + stompClient = Stomp.over(socket); + stompClient.connect({}, function (frame) { + setConnected(true); + console.log('Connected: ' + frame); + stompClient.subscribe('/topic/greetings', function (greeting) { + showGreeting(JSON.parse(greeting.body).content); + }); + }); +} + +function disconnect() { + if (stompClient !== null) { + stompClient.disconnect(); + } + setConnected(false); + console.log("Disconnected"); +} + +function sendName() { + stompClient.send("/app/hello", {}, JSON.stringify({'name': $("#name").val()})); +} + +function showGreeting(message) { + $("#greetings").append("" + message + ""); +} + +$(function () { + $("form").on('submit', function (e) { + e.preventDefault(); + }); + $( "#connect" ).click(function() { connect(); }); + $( "#disconnect" ).click(function() { disconnect(); }); + $( "#send" ).click(function() { sendName(); }); +}); \ No newline at end of file diff --git a/study-spring-boot-web-socket/src/main/resources/static/index.html b/study-spring-boot-web-socket/src/main/resources/static/index.html new file mode 100644 index 000000000..b61326753 --- /dev/null +++ b/study-spring-boot-web-socket/src/main/resources/static/index.html @@ -0,0 +1,53 @@ + + + + Hello WebSocket + + + + + + + + + +
+
+
+
+
+ + + +
+
+
+
+
+
+ + +
+ +
+
+
+
+
+ + + + + + + + +
Greetings
+
+
+
+ + \ No newline at end of file diff --git a/study-spring-boot-websocket/pom.xml b/study-spring-boot-websocket/pom.xml index b77cc7004..85da5997f 100644 --- a/study-spring-boot-websocket/pom.xml +++ b/study-spring-boot-websocket/pom.xml @@ -17,8 +17,8 @@ UTF-8 - 1.7 - 1.7 + 1.8 + 1.8 diff --git a/study-spring-boot-zipkin/pom.xml b/study-spring-boot-zipkin/pom.xml index 523d281ff..f657e1e6d 100644 --- a/study-spring-boot-zipkin/pom.xml +++ b/study-spring-boot-zipkin/pom.xml @@ -17,6 +17,8 @@ UTF-8 + 1.8 + 1.8 2.0.1.RELEASE diff --git a/study-spring-boot/pom.xml b/study-spring-boot/pom.xml index 0beb926a9..ab27c416a 100644 --- a/study-spring-boot/pom.xml +++ b/study-spring-boot/pom.xml @@ -15,6 +15,8 @@ UTF-8 + 1.8 + 1.8 2.0.1.RELEASE diff --git a/study-spring-cloud-circuit-breaker/README.md b/study-spring-cloud-circuit-breaker/README.md new file mode 100644 index 000000000..5f3f4eb8a --- /dev/null +++ b/study-spring-cloud-circuit-breaker/README.md @@ -0,0 +1,4 @@ +# study-spring-cloud-circuit-breaker + +## 参考链接 +https://spring.io/guides/gs/cloud-circuit-breaker/ diff --git a/study-spring-cloud-circuit-breaker/pom.xml b/study-spring-cloud-circuit-breaker/pom.xml new file mode 100644 index 000000000..38792604c --- /dev/null +++ b/study-spring-cloud-circuit-breaker/pom.xml @@ -0,0 +1,113 @@ + + + + 4.0.0 + + + study + com.bage + 0.0.1-SNAPSHOT + + + study-spring-cloud-circuit-breaker + study-spring-cloud-circuit-breaker + http://www.example.com + + + + UTF-8 + 1.8 + 1.8 + + 2.5.3 + 2020.0.6 + + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-log4j2 + + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + org.springframework.boot + spring-boot-starter-test + + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-starter-webflux + + + org.projectlombok + lombok + 1.18.20 + true + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.springframework.cloud + spring-cloud-starter-circuitbreaker-reactor-resilience4j + + + org.springframework.boot + spring-boot-starter-webflux + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + + + org.springframework.boot + spring-boot-dependencies + ${spring.boot.base.version} + pom + import + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring.cloud.version} + pom + import + + + + + + diff --git a/study-spring-cloud-circuit-breaker/src/main/java/com/bage/App.java b/study-spring-cloud-circuit-breaker/src/main/java/com/bage/App.java new file mode 100644 index 000000000..65b45b532 --- /dev/null +++ b/study-spring-cloud-circuit-breaker/src/main/java/com/bage/App.java @@ -0,0 +1,13 @@ +package com.bage; + +/** + * Hello world! + * + */ +public class App +{ + public static void main( String[] args ) + { + System.out.println( "Hello World!" ); + } +} diff --git a/study-spring-cloud-circuit-breaker/src/main/java/com/bage/BookService.java b/study-spring-cloud-circuit-breaker/src/main/java/com/bage/BookService.java new file mode 100644 index 000000000..0cd868afb --- /dev/null +++ b/study-spring-cloud-circuit-breaker/src/main/java/com/bage/BookService.java @@ -0,0 +1,32 @@ +package hello; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import reactor.core.publisher.Mono; + +import org.springframework.cloud.client.circuitbreaker.ReactiveCircuitBreaker; +import org.springframework.cloud.client.circuitbreaker.ReactiveCircuitBreakerFactory; +import org.springframework.stereotype.Service; +import org.springframework.web.reactive.function.client.WebClient; + +@Service +public class BookService { + + private static final Logger LOG = LoggerFactory.getLogger(BookService.class); + + + private final WebClient webClient; + private final ReactiveCircuitBreaker readingListCircuitBreaker; + + public BookService(ReactiveCircuitBreakerFactory circuitBreakerFactory) { + this.webClient = WebClient.builder().baseUrl("http://localhost:8080").build(); + this.readingListCircuitBreaker = circuitBreakerFactory.create("recommended"); + } + + public Mono readingList() { + return readingListCircuitBreaker.run(webClient.get().uri("/recommended").retrieve().bodyToMono(String.class), throwable -> { + LOG.warn("Error making request to book service", throwable); + return Mono.just("Cloud Native Java (O'Reilly)"); + }); + } +} \ No newline at end of file diff --git a/study-spring-cloud-circuit-breaker/src/main/java/com/bage/BookstoreApplication.java b/study-spring-cloud-circuit-breaker/src/main/java/com/bage/BookstoreApplication.java new file mode 100644 index 000000000..6a12144cd --- /dev/null +++ b/study-spring-cloud-circuit-breaker/src/main/java/com/bage/BookstoreApplication.java @@ -0,0 +1,22 @@ +package hello; + +import reactor.core.publisher.Mono; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.RequestMapping; + +@RestController +@SpringBootApplication +public class BookstoreApplication { + + @RequestMapping(value = "/recommended") + public Mono readingList(){ + return Mono.just("Spring in Action (Manning), Cloud Native Java (O'Reilly), Learning Spring Boot (Packt)"); + } + + public static void main(String[] args) { + SpringApplication.run(BookstoreApplication.class, args); + } +} \ No newline at end of file diff --git a/study-spring-cloud-circuit-breaker/src/main/java/com/bage/ReadingApplication.java b/study-spring-cloud-circuit-breaker/src/main/java/com/bage/ReadingApplication.java new file mode 100644 index 000000000..7af3e2c13 --- /dev/null +++ b/study-spring-cloud-circuit-breaker/src/main/java/com/bage/ReadingApplication.java @@ -0,0 +1,25 @@ +package hello; + +import reactor.core.publisher.Mono; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.reactive.function.client.WebClient; + +@RestController +@SpringBootApplication +public class ReadingApplication { + + @RequestMapping("/to-read") + public Mono toRead() { + return WebClient.builder().build() + .get().uri("http://localhost:8080/recommended").retrieve() + .bodyToMono(String.class); + } + + public static void main(String[] args) { + SpringApplication.run(ReadingApplication.class, args); + } +} \ No newline at end of file diff --git a/study-spring-cloud-circuit-breaker/src/test/java/com/bage/AppTest.java b/study-spring-cloud-circuit-breaker/src/test/java/com/bage/AppTest.java new file mode 100644 index 000000000..9216cfd04 --- /dev/null +++ b/study-spring-cloud-circuit-breaker/src/test/java/com/bage/AppTest.java @@ -0,0 +1,20 @@ +package com.bage; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +/** + * Unit test for simple App. + */ +public class AppTest +{ + /** + * Rigorous Test :-) + */ + @Test + public void shouldAnswerWithTrue() + { + assertTrue( true ); + } +} diff --git a/study-spring-dependencies/pom.xml b/study-spring-dependencies/pom.xml index 857cb5926..f9b49d424 100644 --- a/study-spring-dependencies/pom.xml +++ b/study-spring-dependencies/pom.xml @@ -13,6 +13,8 @@ UTF-8 + 1.8 + 1.8 4.3.9.RELEASE UTF-8 3.0-alpha-1 diff --git a/study-spring-email/pom.xml b/study-spring-email/pom.xml index 6d84230b0..ae0311544 100644 --- a/study-spring-email/pom.xml +++ b/study-spring-email/pom.xml @@ -16,6 +16,8 @@ http://www.example.com UTF-8 + 1.8 + 1.8 2.0.1.RELEASE diff --git a/study-spring-session/pom.xml b/study-spring-session/pom.xml index 53ff8f9ef..0b9e47c5b 100644 --- a/study-spring-session/pom.xml +++ b/study-spring-session/pom.xml @@ -15,6 +15,8 @@ UTF-8 + 1.8 + 1.8 5.0.9.RELEASE UTF-8 3.0-alpha-1 diff --git a/study-spring/pom.xml b/study-spring/pom.xml index b77ab24b7..077db7040 100644 --- a/study-spring/pom.xml +++ b/study-spring/pom.xml @@ -13,6 +13,8 @@ http://maven.apache.org UTF-8 + 1.8 + 1.8 4.3.9.RELEASE UTF-8 3.0-alpha-1 diff --git a/study-springmvc-java/pom.xml b/study-springmvc-java/pom.xml index 94a3af8e9..fd0d48e0c 100644 --- a/study-springmvc-java/pom.xml +++ b/study-springmvc-java/pom.xml @@ -14,6 +14,8 @@ UTF-8 + 1.8 + 1.8 4.3.9.RELEASE UTF-8 3.0-alpha-1 diff --git a/study-sql/pom.xml b/study-sql/pom.xml index 6a8654523..322f11bfe 100644 --- a/study-sql/pom.xml +++ b/study-sql/pom.xml @@ -24,6 +24,8 @@ http://maven.apache.org UTF-8 + 1.8 + 1.8 diff --git a/study-sql/src/main/resources/init.sql b/study-sql/src/main/resources/init.sql new file mode 100644 index 000000000..9eb068cd3 --- /dev/null +++ b/study-sql/src/main/resources/init.sql @@ -0,0 +1,9 @@ + +-- index +alter table sl_test add key idx_first_name(first_name); +show indexes from sl_test; + +-- init data +insert sl_test(id,sex,birth,age,first_name,last_name) values (1,'男','2022-01-01 00:00:00',1,'bage','lu'); +insert sl_test(id,sex,birth,age,first_name,last_name) values (2,'女','2020-01-01 00:00:00',3,'bage3','lu'); + diff --git a/study-sql/src/main/resources/schema.sql b/study-sql/src/main/resources/schema.sql new file mode 100644 index 000000000..c88f3a30a --- /dev/null +++ b/study-sql/src/main/resources/schema.sql @@ -0,0 +1,10 @@ + +CREATE TABLE sl_test( +id SERIAL, +sex varchar(64), +birth date, +age int, +first_name VARCHAR(64), +last_name VARCHAR(64) +); + diff --git a/study-sso-baeldung-auth-server/pom.xml b/study-sso-baeldung-auth-server/pom.xml index a51c9e21c..01d0110a9 100644 --- a/study-sso-baeldung-auth-server/pom.xml +++ b/study-sso-baeldung-auth-server/pom.xml @@ -13,6 +13,8 @@ UTF-8 + 1.8 + 1.8 1.5.10.RELEASE diff --git a/study-sso-baeldung-client1/pom.xml b/study-sso-baeldung-client1/pom.xml index b03aece75..173d2f108 100644 --- a/study-sso-baeldung-client1/pom.xml +++ b/study-sso-baeldung-client1/pom.xml @@ -14,6 +14,8 @@ UTF-8 + 1.8 + 1.8 1.5.10.RELEASE diff --git a/PPT.md b/study-summary/PPT.md similarity index 100% rename from PPT.md rename to study-summary/PPT.md diff --git a/README-OS-Reinstall.md b/study-summary/README-OS-Reinstall.md similarity index 100% rename from README-OS-Reinstall.md rename to study-summary/README-OS-Reinstall.md diff --git a/README-WeChat-Developer.md b/study-summary/README-WeChat-Developer.md similarity index 100% rename from README-WeChat-Developer.md rename to study-summary/README-WeChat-Developer.md diff --git a/study-summary/README-bilibili.md b/study-summary/README-bilibili.md new file mode 100644 index 000000000..e38b02462 --- /dev/null +++ b/study-summary/README-bilibili.md @@ -0,0 +1,305 @@ +# 进度备忘 + + + +面试汇总: + +Spring Cloud: + +http://c.biancheng.net/springcloud/eureka.html + +http://c.biancheng.net/springcloud/ + +https://blog.csdn.net/yanpenglei/article/details/121994645 + +https://blog.csdn.net/m0_48795607/article/details/115917190 + + + +MySQL + +https://www.cnblogs.com/setalone/p/14851000.html + +索引下推:https://baijiahao.baidu.com/s?id=1716515482593299829&wfr=spider&for=pc + +DB Log : + +https://www.baidu.com/baidu?tn=monline_7_dg&ie=utf-8&wd=%E6%95%B0%E6%8D%AE%E5%BA%93%E6%97%A5%E5%BF%97 + + + +Tomcat + +https://baijiahao.baidu.com/s?id=1707755764671407613&wfr=spider&for=pc + +http://www.javashuo.com/article/p-feodoefp-md.html + +https://blog.csdn.net/zps66/article/details/117673727 + +https://server.it168.com/a2017/0901/3168/000003168567.shtml + + + +场景设计: + +https://github.com/Snailclimb/JavaGuide-Interview/blob/master/docs/f-2%E7%B3%BB%E7%BB%9F%E8%AE%BE%E8%AE%A1%E9%9D%A2%E8%AF%95%E6%8C%87%E5%8C%97.md + + + +Kafaka: + +https://github.com/Snailclimb/JavaGuide-Interview/blob/master/docs/e-3kafka.md + +https://blog.csdn.net/yanpenglei/article/details/121862772 + +https://zhuanlan.zhihu.com/p/469002514 + + + +https://github.com/Snailclimb/JavaGuide + +https://hadyang.com/interview/docs/architecture/distributed/dubbo/ + +https://github.com/yuanguangxin/LeetCode + +https://github.com/gzc426/Java-Interview/tree/master/docs + +https://github.com/Snailclimb/JavaGuide-Interview/tree/master/docs + +https://github.com/itdevbooks/tech + + + +当前进度:11 + +https://zhuanlan.zhihu.com/p/64147696 + + + +Spring + +https://www.bilibili.com/video/BV1y541127Yg?spm_id_from=333.851.b_7265636f6d6d656e64.6 + + + +压测JMetter + +https://www.bilibili.com/video/BV1st411Y7QW?spm_id_from=333.337.search-card.all.click + +https://www.bilibili.com/video/BV1hL4y1B7Et?spm_id_from=333.337.search-card.all.click + + + +https://leetcode-cn.com/problems/UHnkqh/ + +问答 + +https://www.bilibili.com/video/BV1iV411p7LU?p=134 + +问答 + +https://www.bilibili.com/video/BV1iV411p7LU?p=39 + +https://www.bilibili.com/video/BV1iV411p7LU?p=38 + +https://www.bilibili.com/video/BV1iV411p7LU?p=36 + +https://www.bilibili.com/video/BV1iV411p7LU?p=35 + +https://www.bilibili.com/video/BV1iV411p7LU?p=34&spm_id_from=pageDriver + +https://www.bilibili.com/video/BV1iV411p7LU?p=33 + +https://www.bilibili.com/video/BV1iV411p7LU?p=32 + +https://www.bilibili.com/video/BV1iV411p7LU?p=31 + +https://www.bilibili.com/video/BV1iV411p7LU?p=30&spm_id_from=pageDriver + +https://www.bilibili.com/video/BV1iV411p7LU?p=24&spm_id_from=pageDriver + +https://www.bilibili.com/video/BV1iV411p7LU?p=9&spm_id_from=pageDriver + +https://www.bilibili.com/video/BV1iV411p7LU?from=search&seid=7880316255452534004&spm_id_from=333.337.0.0 + +注册中心、分布式事务、网管 + +ShardingSphere + +https://www.bilibili.com/video/BV1XU4y177De?from=search&seid=5629134683248808430&spm_id_from=333.337.0.0 + +https://www.bilibili.com/video/BV1uz4y1S7ov?p=2 + +Spring Boot + +https://www.bilibili.com/video/BV1nK4y1H7g9?p=189 + +https://www.bilibili.com/video/BV1nK4y1H7g9?p=186 + +https://www.bilibili.com/video/BV1nK4y1H7g9?p=185 + +https://www.bilibili.com/video/BV1nK4y1H7g9?p=184 + +Spring Cloud + +https://www.bilibili.com/video/BV1nK4y1H7g9?p=182 + +https://www.bilibili.com/video/BV1nK4y1H7g9?p=178&spm_id_from=pageDriver + +https://www.bilibili.com/video/BV1nK4y1H7g9?p=175 + +https://www.bilibili.com/video/BV1nK4y1H7g9?p=174&spm_id_from=pageDriver + +https://www.bilibili.com/video/BV1nK4y1H7g9?p=173&spm_id_from=pageDriver + +https://www.bilibili.com/video/BV1nK4y1H7g9?p=170&spm_id_from=pageDriver + +Spring Cloud Nacoss + +https://www.bilibili.com/video/BV1nK4y1H7g9?p=167&spm_id_from=pageDriver + +https://www.bilibili.com/video/BV1nK4y1H7g9?p=165 + + + +dubbo【暂时段落】 + +https://www.bilibili.com/video/BV1nK4y1H7g9?p=142&spm_id_from=pageDriver + +https://www.bilibili.com/video/BV1nK4y1H7g9?p=140&spm_id_from=pageDriver + +https://www.bilibili.com/video/BV1nK4y1H7g9?p=138&spm_id_from=pageDriver + +https://www.bilibili.com/video/BV1nK4y1H7g9?p=137&spm_id_from=pageDriver + +https://www.bilibili.com/video/BV1nK4y1H7g9?p=134 + + + +Tomcat + +https://www.bilibili.com/video/BV1nK4y1H7g9?p=27&spm_id_from=pageDriver + +https://www.bilibili.com/video/BV1nK4y1H7g9?p=25&spm_id_from=pageDriver + +https://www.bilibili.com/video/BV1nK4y1H7g9?p=24&spm_id_from=pageDriver + +https://www.bilibili.com/video/BV1nK4y1H7g9?p=20 + +JVM + +https://www.bilibili.com/video/BV1r3411Y7fd?spm_id_from=333.851.b_7265636f6d6d656e64.4 + + + + + +分布式ID + +https://www.bilibili.com/video/BV1nK4y1H7g9?p=154 + +分布式事务【未完成】 + +https://www.bilibili.com/video/BV1nK4y1H7g9?p=148 + + + +Spring MVC + +https://www.bilibili.com/video/BV1nK4y1H7g9?p=91&spm_id_from=pageDriver + +https://www.bilibili.com/video/BV1nK4y1H7g9?p=90&spm_id_from=pageDriver + +https://www.bilibili.com/video/BV1nK4y1H7g9?p=89 + +Spring AoP + +https://www.bilibili.com/video/BV1nK4y1H7g9?p=77&spm_id_from=pageDriver + +https://www.bilibili.com/video/BV1nK4y1H7g9?p=74 + +Spring 生命周期 +https://www.bilibili.com/video/BV1nK4y1H7g9?p=69&spm_id_from=pageDriver + +https://www.bilibili.com/video/BV1nK4y1H7g9?p=50 + +线程池 +https://www.bilibili.com/video/BV1nK4y1H7g9?p=58 + +Spring 手写 +https://www.bilibili.com/video/BV1nK4y1H7g9?p=68&spm_id_from=pageDriver + +常用点 +https://www.bilibili.com/video/BV1V44y1i7L8?p=2 + + +## 总结 +MySQL +1、 索引、联合索引 +2、 log日志 +3、 版本控制 +4、 + +Java 锁 +自旋锁 》 偏向锁 》 重量锁 + +Java 线程池 +优化?? +注意事项 + +条件短点 + + +# 常用链接 + + + +- 搜索结果 + +https://search.bilibili.com/all?keyword=JAVA%E9%9D%A2%E8%AF%95%E9%A2%982021&from_source=webtop_search&spm_id_from=333.851&order=totalrank&duration=4&tids_1=0 + + + + + +# [活动作品](https://www.bilibili.com/blackboard/techhunters3.html)2022年最新版Java面试题突击视频教程,不管工作几年还是应届生必看的Java面试必考真题【最强Java面试宝典】赶紧收藏起来! + + + +https://www.bilibili.com/video/BV1UU4y1u7fB?p=2&spm_id_from=pageDriver + + + +- 面试系列!! + +# Alibaba最新Java面试137题汇总详解__对标P7,全学完随便拿50Koffer + +https://www.bilibili.com/video/BV1V44y1i7L8?from=search&seid=16808638854944282623&spm_id_from=333.337.0.0 + + + +# [活动作品](https://www.bilibili.com/video/BV1aV411W7U2?from=search&seid=7880316255452534004&spm_id_from=333.337.0.0)B站最全面Java面试500讲(涵盖所有Java核心面试知识点),立刻收藏! + +https://www.bilibili.com/video/BV1aV411W7U2?from=search&seid=7880316255452534004&spm_id_from=333.337.0.0 + + + +- 马士兵 + +# 终于把阿里1350道Java面试题都讲明白了:开源视频+笔记+源码+文档+解析图 + +https://www.bilibili.com/video/BV1G44y1v7Hz?p=1 + + + +- 面试题目汇总-- + +https://www.bilibili.com/video/BV1iV411p7LU?from=search&seid=7880316255452534004&spm_id_from=333.337.0.0 + + + +https://www.bilibili.com/video/BV1w341187cF?from=search&seid=7880316255452534004&spm_id_from=333.337.0.0 + + + +https://www.bilibili.com/video/BV1444y1x7gJ?from=search&seid=591244058940940941&spm_id_from=333.337.0.0 diff --git a/study-summary/README-mq.md b/study-summary/README-mq.md new file mode 100644 index 000000000..00c6382b5 --- /dev/null +++ b/study-summary/README-mq.md @@ -0,0 +1,6 @@ +# Study-mq # + + +Kafaka: + +参考链接: https://blog.csdn.net/u012588879/article/details/122957237 \ No newline at end of file diff --git a/README-paxos.md b/study-summary/README-paxos.md similarity index 100% rename from README-paxos.md rename to study-summary/README-paxos.md diff --git a/study-summary/README-structure.md b/study-summary/README-structure.md new file mode 100644 index 000000000..63e213aa1 --- /dev/null +++ b/study-summary/README-structure.md @@ -0,0 +1,16 @@ +# 系统结构 + +技术要素 + +团队建设 + +发展 + +技术趋势 + +招兵买马 + + + + + diff --git a/study-summary/README.md b/study-summary/README.md index 94c481bff..9715805b9 100644 --- a/study-summary/README.md +++ b/study-summary/README.md @@ -2,6 +2,40 @@ PPT 讲解 STAR 法则,背景、遇到问题、采取的方案、取得的效果 +NEW STRAT +UT 编写用例 +管理学知识 +技术技能 +Cauch 教练管理方式 +裸心会 +调查问卷 + +代理: https://blog.csdn.net/lx1315998513/article/details/120641124 + + + +## 员工找回 + +Spring Security ,干掉profile 目录等 + +Spring Security 使用自定义 数据库表结构 + + + +## Mac 使用技巧 +https://zhuanlan.zhihu.com/p/89987302 + + + +查看端口占用: + +lsof -i:1234 + + + +58826 + + ## 常用连接 @@ -29,7 +63,7 @@ https://github.com/akullpp/awesome-java https://github.com/AobingJava/JavaFamily - +https://github.com/tuteng/Best-websites-a-programmer-should-visit-zh#interview-preparation ## [Java 基础](https://github.com/bage2014/interview/blob/master/README-Java.md) ## @@ -66,6 +100,10 @@ JDK各个版本特性; 数据库索引; +数据库读取,按页读取 + +各种索引对比 + 索引结构原理; innerDB等; @@ -142,6 +180,28 @@ CompletableFuture; ## [JVM](https://github.com/bage2014/interview/blob/master/README-Jvm.md) ## 常用理论知识,前世今生,开源组件; +### 对象创建 + +**常量池校验** + +**内存分配** + +1. 指针碰撞 +2. 空闲列表 + +内存分配线程安全 + +1. CAS线程枷锁实现 +2. TLAB 本地线程分配缓冲 + +**零值初始化** + +**对象元信息初始化** + +**Java 线程初始化** + + + ### 内存划分 ### 了解目标:基本概念、主要特点、常见异常 diff --git a/study-summary/pom.xml b/study-summary/pom.xml index 1fd030073..68c0c1d52 100644 --- a/study-summary/pom.xml +++ b/study-summary/pom.xml @@ -17,8 +17,8 @@ UTF-8 - 1.7 - 1.7 + 1.8 + 1.8 diff --git a/study-swagger-cxf/pom.xml b/study-swagger-cxf/pom.xml index 4e34c46bf..3e4224a03 100644 --- a/study-swagger-cxf/pom.xml +++ b/study-swagger-cxf/pom.xml @@ -102,7 +102,7 @@ com.fasterxml.jackson.core jackson-databind - 2.9.5 + 2.12.7.1 com.fasterxml.jackson.core diff --git a/study-tencent-share/pom.xml b/study-tencent-share/pom.xml index bb611f51f..6aad43743 100644 --- a/study-tencent-share/pom.xml +++ b/study-tencent-share/pom.xml @@ -17,8 +17,8 @@ UTF-8 - 1.7 - 1.7 + 1.8 + 1.8 diff --git a/study-test-restdoc/pom.xml b/study-test-restdoc/pom.xml index 3ef952e51..68edfe7ca 100644 --- a/study-test-restdoc/pom.xml +++ b/study-test-restdoc/pom.xml @@ -17,6 +17,8 @@ UTF-8 + 1.8 + 1.8 2.0.1.RELEASE diff --git a/study-test/README.md b/study-test/README.md index 3c6e50903..98350b84a 100644 --- a/study-test/README.md +++ b/study-test/README.md @@ -9,4 +9,6 @@ ### 验证(命令行) ### - $ findbugs -textui -low -html -output myreport.html C:\Users\luruihua\git\study\study\study-algorithm \ No newline at end of file + $ findbugs -textui -low -html -output myreport.html C:\Users\luruihua\git\study\study\study-algorithm + + diff --git a/study-test/pom.xml b/study-test/pom.xml index ba0f604dd..8539d9a1c 100644 --- a/study-test/pom.xml +++ b/study-test/pom.xml @@ -12,6 +12,8 @@ http://maven.apache.org UTF-8 + 1.8 + 1.8 diff --git a/study-todo/README-my-app-card.md b/study-todo/README-my-app-card.md new file mode 100644 index 000000000..25ca0b52e --- /dev/null +++ b/study-todo/README-my-app-card.md @@ -0,0 +1,238 @@ +# study-my-app-name-card # + +用户名片功能模块 + +## 功能整理 + + + +## 过程备忘 + +### 用户研究与需求分析【ALL】 + +- 必要充要 + +试试水 + +应用实践 + +技术门槛较低 + + + +- 用户分析 + +所有用户、特别是年轻人?找工作的人?【后面可以绑定简历、弄一个建立功能模块】 + +年龄段,任意年龄段 + +使用频率,不限制 + + + +- 竞品分析 + +列表: http://www.downcc.com/k/mingpianapp/ + +https://www.nongjia888.com/s/zj-16047718 + + + +重点分析: + +名片全能王免费版 + 微名片手机客户端 + + +优势: + +拍照识别导入、更新提醒、名片交换、好友推荐、人脉管理 + +不足: + +**调整点** + +用户定位过于商业?支持普通人、同学、朋友、通讯录好友 + +信息不够丰富,增加、人生简历、、、基本信息【不需要权限】、原籍、居住地、手机号、邮箱、微信、小学、初中、高中、大学、家族、公司、荣誉?证书? + +更新推送限制,手机号等更新,个性化推送,,比如只推送给家人,,更新保存时候,进行提醒??推送给那些TAG + +部分收费,将免费处理!! + +**新增点** + +无Tag 标签管理,标签为王,所有的区别都是标签 + +无子标签概念,标签关系,相互关联 + +无权限管理,需要支持普通关系不能查看某些信息 + +信息查看,需要进行申请,同意了才可以查看,,同时需要提供自己的信息? + +人员点点赞、踩踩、吃瓜【暂不开放评论功能】 + +实名认证??看情况 + +数据存储,需要加密!每个人不一样的策略,至少不是相同的 + +管理员模式??【比如班级、家族有管理员模式】 + +动态发布?可以发表个人动态 + + + +- 用户定位 + +全体人民 + +男女老少 + +业界精英 + +- 产品优势 + +面向用户更加宽广 + +功能更加强大 + +方便联系?查找?同城老乡?同城同学?同学分布? + + + +- 核心功能整理 + +用户注册【邮箱】、登陆、忘记密码,5天 + +个人信息维护,5天 + +动态管理,3天 + +标签管理, 10天 + +首页,,好友动态? 3天 + +信息更新推送,5天 + +好友管理?好友关系?临时好友,5天 + +留言管理,5天 + +- 开发周期 + +调研,5天 + +UI 页面,7天 + +研发;后端研发 30天 + + + +- 成本预估 + +PC 和 APP 版本?两个版本? + +云主机 + +域名 + +备案 + +推广运营 + +留言处理 + + + +- 排期情况 + +重点、核心点先做?? + +不断补全功能? + + + +### 产品原型设计【PM】 + +- 产品理念 + +- 产品信息 + +产品口号,简介,等 + +- 基本功能 + +表格显示::: + +- 基本页面 + +网站页面、原型设计 + +### UI视觉设计【UI】 + +logo + +页面风格、基调、基本色 + +页面设计 + +交互体验 + +### 软件基础搭建【OPS+DEV】 + +数据库 + +云主机 + +软件环境搭建 + +服务架构、请求链路 + +### 服务端开发【DEV】 + +学校?地址?基础数据来源接口?? + +应用架构 + +技术选型 + +可行性调研 + +### 客户端开发【DEV】 + +IOS/Android + +技术调研 + +### 程序测试【TEST】 + +服务端测试 + +客户端测试 + +压力测试 + +安全测试 + +### 应用上线【TECH】 + +发布到生产环境 + +上传到应用商店 + + + +### 推广运营 + +todo: 之前整理过一版本,,后面补上 + + + +### 维护更新 + + + +### 服务下线 + + + + + diff --git a/study-todo/README-my-app-common.md b/study-todo/README-my-app-common.md new file mode 100644 index 000000000..f5f57c951 --- /dev/null +++ b/study-todo/README-my-app-common.md @@ -0,0 +1,156 @@ +# study-my-app- common # + +公共功能模块 + + + +## 功能整理 + + + +| 类别 | 功能点 | 状态 | 说明 | 优先级 | +| -------- | -------- | ---- | ------------------------------ | ------ | +| 用户登陆 | 用户登陆 | todo | 根据邮箱、或者 用户ID 登陆 | | +| | 用户注册 | todo | 根据邮箱注册 | | +| | 重置密码 | todo | | | +| | 用户简介 | todo | 用户基本信息维护、包括用户头像 | | +| | 隐私条款 | todo | | | + +页面实现 + +登陆 + +注册 + +重置密码 + + + +## 过程备忘 + +- 核心功能整理 + +用户注册【邮箱】、登陆、忘记密码,5天 + +个人信息【基本信息】维护,5天 + +留言管理,5天 + +- 开发周期 + +UI 页面,1天 + +研发;后端研发 7天 + + + +- 成本预估 + +PC 和 APP 版本?两个版本? + +云主机 + +域名 + +备案 + +推广运营 + +留言处理 + + + +- 排期情况 + +重点、核心点先做?? + +不断补全功能? + + + +### 产品原型设计【PM】 + +- 产品理念 + +- 产品信息 + +产品口号,简介,等 + +- 基本功能 + +表格显示::: + +- 基本页面 + +网站页面、原型设计 + +### UI视觉设计【UI】 + +logo + +页面风格、基调、基本色 + +页面设计 + +交互体验 + +### 软件基础搭建【OPS+DEV】 + +数据库 + +云主机 + +软件环境搭建 + +服务架构、请求链路 + +### 服务端开发【DEV】 + +学校?地址?基础数据来源接口?? + +应用架构 + +技术选型 + +可行性调研 + +### 客户端开发【DEV】 + +IOS/Android + +技术调研 + +### 程序测试【TEST】 + +服务端测试 + +客户端测试 + +压力测试 + +安全测试 + +### 应用上线【TECH】 + +发布到生产环境 + +上传到应用商店 + + + +### 推广运营 + +todo: 之前整理过一版本,,后面补上 + + + +### 维护更新 + + + +### 服务下线 + + + + + diff --git a/study-todo/README-my-app.md b/study-todo/README-my-app.md new file mode 100644 index 000000000..6365adc5e --- /dev/null +++ b/study-todo/README-my-app.md @@ -0,0 +1,182 @@ +# study-my-app # + +## 参考链接 + +APP应用软件开发流程是怎样的? https://zhuanlan.zhihu.com/p/21914970 + +https://www.zhihu.com/question/21325644 + + + +http://www.680.com/it/1912/shouji-121637.html + +https://jingyan.baidu.com/article/915fc41482139610394b20cc.html + +https://baijiahao.baidu.com/s?id=1730168625302231844&wfr=spider&for=pc + +APP开发外包,各个流程详细说明 https://www.bilibili.com/read/cv16571523 + + + +## 概述 + +### 本文目标 + +### 名次解释 + +PM 产品经理 + +UI 页面设计师 + +DEV 研发 + +TEST 测试 + +??运营人员 + +OPS 运维人员 + +### 参与角色 + +产品经理 + +页面设计师 + +研发人员、服务端 + 客户端 + +测试人员 + +运营人员 + +OPS 人员、应用部署发布等 + +App从无到有,直接参与的人员有哪些? + +老板/产品总监 + +市场人员 + +技术总监 + +## 基本过程 + +PRD 绘制工具 + +摹客 https://www.mockplus.cn/example/rp + + + +### 用户研究与需求分析【ALL】 + +- 必要充要 + +试试水 + +应用实践 + +- 用户分析 + +所有用户 + +年龄段,任意年龄段 + +使用频率,不限制 + +- 竞品分析 + +- 用户定位 + +- 产品优势 + + + +- 开发周期 +- 成本预估 +- 排期情况 + + + +### 产品原型设计【PM】 + +- 产品理念 + +- 产品信息 + +产品口号,简介,等 + +- 基本功能 + +表格显示::: + +- 基本页面 + +网站页面、原型设计 + +### UI视觉设计【UI】 + +logo + +页面风格、基调、基本色 + +页面设计 + +交互体验 + +### 软件基础搭建【OPS+DEV】 + +数据库 + +云主机 + +软件环境搭建 + +服务架构、请求链路 + +### 服务端开发【DEV】 + +应用架构 + +技术选型 + +可行性调研 + +### 客户端开发【DEV】 + +IOS/Android + +技术调研 + +### 程序测试【TEST】 + +服务端测试 + +客户端测试 + +压力测试 + +安全测试 + +### 应用上线【TECH】 + +发布到生产环境 + +上传到应用商店 + + + +### 推广运营 + +todo: 之前整理过一版本,,后面补上 + + + +### 维护更新 + + + +### 服务下线 + + + + + diff --git a/study-todo/README-mysql.md b/study-todo/README-mysql.md new file mode 100644 index 000000000..4c58e45ee --- /dev/null +++ b/study-todo/README-mysql.md @@ -0,0 +1,8 @@ +# study- Mysql # +TODO + +## 备忘 + +https://www.bilibili.com/video/BV1Ca411R7GG?p=46&vd_source=72424c3da68577f00ea40a9e4f9001a1 + + diff --git a/study-todo/README-redis.md b/study-todo/README-redis.md new file mode 100644 index 000000000..90bea6a95 --- /dev/null +++ b/study-todo/README-redis.md @@ -0,0 +1,14 @@ +# study- Redis # +TODO + +## 备忘 + +https://www.bilibili.com/video/BV1ed4y1g7XU?spm_id_from=333.1007.tianma.2-1-4.click&vd_source=72424c3da68577f00ea40a9e4f9001a1 + + + + + + + + diff --git a/study-todo/README-rocket-mq.md b/study-todo/README-rocket-mq.md new file mode 100644 index 000000000..9253cdb88 --- /dev/null +++ b/study-todo/README-rocket-mq.md @@ -0,0 +1,41 @@ +# study-RocketMQ # +TODO + +常见面试题目?? + + + +## 参考链接 + + + +20220820备忘 + +https://www.bilibili.com/video/BV11o4y1D7DY?p=22&spm_id_from=pageDriver&vd_source=72424c3da68577f00ea40a9e4f9001a1 + +https://www.bilibili.com/video/BV11o4y1D7DY?p=21&spm_id_from=pageDriver&vd_source=72424c3da68577f00ea40a9e4f9001a1 + +https://www.bilibili.com/video/BV11o4y1D7DY?p=19&spm_id_from=pageDriver&vd_source=72424c3da68577f00ea40a9e4f9001a1 + +https://www.bilibili.com/video/BV11o4y1D7DY?p=18&spm_id_from=pageDriver&vd_source=72424c3da68577f00ea40a9e4f9001a1 + +20220810备忘 + +https://www.bilibili.com/video/BV11o4y1D7DY?p=17&spm_id_from=pageDriver&vd_source=72424c3da68577f00ea40a9e4f9001a1 + +https://www.bilibili.com/video/BV11o4y1D7DY?p=14&vd_source=72424c3da68577f00ea40a9e4f9001a1 + +https://www.bilibili.com/video/BV11o4y1D7DY?p=10&vd_source=72424c3da68577f00ea40a9e4f9001a1 + +20220808 备忘 + +https://www.bilibili.com/video/BV11o4y1D7DY?p=15&spm_id_from=pageDriver&vd_source=72424c3da68577f00ea40a9e4f9001a1 + +https://www.bilibili.com/video/BV11o4y1D7DY?p=9&vd_source=72424c3da68577f00ea40a9e4f9001a1 + + + +20220807 备忘 + +https://www.bilibili.com/video/BV11o4y1D7DY?p=2&spm_id_from=pageDriver&vd_source=72424c3da68577f00ea40a9e4f9001a1 + diff --git a/study-todo/README-spring-cloud.md b/study-todo/README-spring-cloud.md new file mode 100644 index 000000000..3530ad010 --- /dev/null +++ b/study-todo/README-spring-cloud.md @@ -0,0 +1,18 @@ +# study- Spring- cloud # +TODO + +## 备忘 + +0901 + +https://www.bilibili.com/video/BV1Ca411R7GG?p=51&vd_source=72424c3da68577f00ea40a9e4f9001a1 + +https://www.bilibili.com/video/BV1Ca411R7GG?p=3&spm_id_from=pageDriver&vd_source=72424c3da68577f00ea40a9e4f9001a1 + + + +## 参考链接 + + + +https://www.bilibili.com/video/BV1H14y147aT?spm_id_from=333.999.0.0 \ No newline at end of file diff --git a/study-todo/README.md b/study-todo/README.md index 4a723193e..7f286d0ba 100644 --- a/study-todo/README.md +++ b/study-todo/README.md @@ -1,9 +1,260 @@ # study-TODO # -代办事项: +# 技术代办 +Jenkins +redis +Kafaka + +PacificA : kafka采用的一致性协议 + +Paxos 一致性协议 + +压测 + +线上问题定位 + +Dump 文件分析 + +Spring Cloud Alibaba 相关环境搭建 + +Docker 版本 + +搭建Spring Cloud 环境 + +注册中心、啥的 【docker版本】 + +Spring Test + + + +核心技能 + +高级JVM 分析 + +线上问题排查 + +常用工具高级用法 + + + +注册中心: + +https://nacos.io/zh-cn/index.html + +http://c.biancheng.net/springcloud/nacos.html + + + + + +## 项目代办 + +| 模块 | 功能概述 | 完成状态 | 时间区间 | 边界条件 | +| ---------- | ---------- | -------- | :-------------------: | ------------------------------------------------------------ | +| 用户登陆 | 账号登陆 | done | 2022/10/23-2022/10/24 | - 根据邮箱、输入正确密码,即可成功登陆
- 校验输入合法性,账号、密码不能为空
- 输入密码错误N【暂定2】次后,需要输入验证码 | +| | 自动登陆 | todo | 2022/10/12-2022/10/? | - 只要存在登陆成功后,token存储到本地
- N【暂定7】天内免登陆,自动续约token
- 自动记录,最近的一次登陆账号信息
- 不记录密码信息 | +| | 退出登陆 | todo | 2022/10/11-2022/10/12 | - 用户退出,退出当前登陆状态,需要重新进行登陆
- 移除所有登陆状态 | +| | | | | | +| 用户注册 | 邮箱注册 | Check | | - 用户注册? | +| | | | | | +| 用户详情 | 登陆状态 | | | - 用户登陆成功后,需要显示用户基本信息
- 用户可以查看、编辑个人详细信息
- 用户可以更新头像或者自定义头像 | +| | | | | | +| 密码重置 | 找回密码 | todo | | - 通过邮箱找回密码? | +| | | | | | +| - 应用设置 | 应用更新 | | | - APP 检查更新
- APP 内可以更新到最新的版本
- 兜底打开浏览器,访问下载链接 | +| | | | | | +| | | | | | +| - 基础建设 | 无网络提示 | todo | | - 无网络连接,给予无网络提示
- 服务器无响应,基于服务器错误提示
- 其他错误,兜底服务器错误提示 | +| | 环境切换 | Done | 2022/10/18-2022/10/21 | - 程序默认初始化好三个环境配置:dev\test\prod
- app 可以进行环境切换 | +| | 环境切换 | ING | 2022/12/06-2022/12/06 | - 开关切换是否开启Mock网络请求 | +| | 公告页面 | todo | | - 公告增删改查维护
- 公告查看
- 公告关闭 | +| | | | | | +| 学习时间 | 学时列表 | todo | | - 查看自己的所有页面 | +| | 新增学时 | todo | | - 新增一个学习规律,比如每节课30分钟,休息10分钟ß | +| | | | | | +| 个人名片 | 名片新增 | todo | | - 特定模板进行添加
- 个性化自定义添加 | +| | 名片查看 | todo | | - 列表查看
- 自定义查看顺序 | +| | 名片分享 | todo | | - 选择特定卡片分享给他人
- 常用模板分享给他人
- 分享到社交媒体 | +| | | | | | +| 用户留言 | 用户留言 | todo | | - 用户给APP 官方留言 | +| | 系统回复 | todo | | - 系统给用户留言进行回复 | +| | 点赞踩踩 | todo | | - 点赞/踩 他人的留言 | +| | | | | | +| 闹钟设置 | 间隔时长 | todo | | - 每固定间隔【比如40分钟】响铃一次,相当于番茄学习 | +| | 休息时间 | todo | | - 固定学习时间后,进入休息时间 | +| | | | | | +| | | | | | +| | | | | | +| | | | | | + + + + + + + +## 技术代办 + +## 代办事项 + +免费开放API + +https://github.com/modood/Administrative-divisions-of-China + +http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/ 官方数据来源 + +https://github.com/WenryXu/ChinaUniversity 高校 + +https://github.com/public-apis/public-apis + + + +学校 + +https://github.com/516134941/cn-universitysrv + +### APP 代办 + +https://github.com/florinpop17/app-ideas APP 想法 + +公告页面【栏】 + +查询列表【返回最近有效的最新公告列表,APP 显示轮播图】 + +APP 测关闭公告,, + + + +一个月内免费登陆??? + +用户注册API + +API 方法 VS Logic 直接调用 + +Auth Server 设置自定义User ID字段 + +统一校验登陆状态; + +名片编辑页面 + +2022年结束!!!完成一个APP 上线 + +基本功能: + +2 个左右主打功能 + +登陆注册 + +留言、更新功能 + + + +后端,,, + +文件上传-待自测 + +Run --app + +app 名字更新确定 + +UI route 设置部分页面拦截登陆 + + + +- 删除demo页 +- 更新验证码输入页面: https://github.com/Vignesh0404/Flutter-UI-Kit/blob/main/Auth-otp-CustomKeypad/lib/verifyOTP.dart +- 微信登陆 https://www.authingyun.com/solutions/wechat +- 登陆注册APP更新页面 + + + + + + + +## Flutter + +Mac 搭建Flutter 环境 + +下载Flutter + +下载Android Studio + + + +配置fluter 环境变量 + + + +安装 Dart 插件、Flutter 插件 + + + +ZipException 处理 + +Flutter run ZipException + +https://www.jianshu.com/p/8e1007e65c66 + +``` + + +``` + + + +## Idea + + + +https://www.baidu.com/s?wd=%E6%9C%89%E4%BB%80%E4%B9%88%E6%96%B0%E9%A2%96app%E6%83%B3%E6%B3%95&pn=20&oq=%E6%9C%89%E4%BB%80%E4%B9%88%E6%96%B0%E9%A2%96app%E6%83%B3%E6%B3%95&ie=utf-8&fenlei=256&rsv_idx=1&rsv_pq=c6ff598c000f331e&rsv_t=fea2kaljO0xoo8gvRpHo%2F67HDIO4yMP4OXIncyW%2BgDAgqJy0rgXbySsUv2M + + + + + +1、、、、、、、、 + +最近网课又出现在我的生活里了。 + + 有个想法,我尽量用简单的语言描述一下。 + + 背景:我选择用ipad+iPhone pencil听网课,有做笔记的习惯 + +**产生的问题:** + + ①当我使用ipad听网课时我就无法做笔记,当我用ipad做笔记时就无法看网课。(同一设备) + + ②分屏功能也不错,但是问题在于分屏的话我的网课画面就会变小,和用手机看有什么区别呢?甚至还不如手机屏幕大。 + + ③当然我也可以用电脑看网课,用pad或本子做笔记,但我更想拥有一个这样的软件,后期我再整理纸质笔记。 + +**关于应用的设想:** + + 这个应用可以**漂浮**在各应用之上,有点悬浮窗的感觉,而这个笔记的纸它本身是一个**透明的**(或者是可以设置为透明的),可以让我在屏幕的各个地方、各个软件上随意做笔记,同时设置截图、录音、加减纸张甚至录制背景视频的**快捷键**。当我打开软件查看我的笔记本时,也可以查看我当时的笔记。 + +作者:风骨可无羡 + +链接:https://www.zhihu.com/question/299878221/answer/1731615384 + +来源:知乎 + +著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 + + + +2、、、、、、、、 + +付费WiFi,到其他地方没流量了,付费给别人的WiFi让别人告诉你密码就可以了,怎么样。 + + + +3、、、、、、、、、 + +应用监控,,正在打开那个应用???? \ No newline at end of file diff --git a/study-todo/my-app-component-app.mrp b/study-todo/my-app-component-app.mrp new file mode 100644 index 000000000..51b7a07e3 Binary files /dev/null and b/study-todo/my-app-component-app.mrp differ diff --git a/study-tomcat/README.md b/study-tomcat/README.md new file mode 100644 index 000000000..9d8aa1e78 --- /dev/null +++ b/study-tomcat/README.md @@ -0,0 +1,181 @@ +# study-Tomcat # +## ***性能优化*** + +参考链接: + +https://www.cnblogs.com/zhuawang/p/5213192.html + +***\*一:Tomcat内存优化,启动时告诉JVM我要一块大内存(调优内存是最直接的方式)\**** + +Windows 下的catalina.bat + +Linux 下的catalina.sh 如: + +JAVA_OPTS='-Xms256m -Xmx512m' + +-Xms JVM初始化堆的大小 + +-Xmx JVM堆的最大值 实际参数大小根据服务器配置或者项目具体设置. + +***\*二:Tomcat 线程优化\**** 在server.xml中 如: + + + +maxThreads="X" 表示最多同时处理X个连接 + +minSpareThreads="X" 初始化X个连接 + +maxSpareThreads="X" 表示如果最多可以有X个线程,一旦超过X个,则会关闭不在需要的线程 + +acceptCount="X" 当同时连接的人数达到maxThreads时,还可以排队,队列大小为X.超过X就不处理 + +***\*三:Tomcat IO优化\**** + +1:同步阻塞IO(JAVA BIO) 同步并阻塞,服务器实现模式为一个连接一个线程(one connection one thread 想想都觉得恐怖,线程可是非常宝贵的资源),当然可以通过线程池机制改善. + +2:JAVA NIO:又分为同步非阻塞IO,异步阻塞IO 与BIO最大的区别one request one thread.可以复用同一个线程处理多个connection(多路复用). + +3:,异步非阻塞IO(Java NIO2又叫AIO) 主要与NIO的区别主要是操作系统的底层区别.可以做个比喻:比作快递,NIO就是网购后要自己到官网查下快递是否已经到了(可能是多次),然后自己去取快递;AIO就是快递员送货上门了(不用关注快递进度)。 + +BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解. + +NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持. + +AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持. + +在server.xml中 + + + +实现对Tomcat的IO切换. + +## **2.** ***\*配置\**** + +参考链接: + +https://www.cnblogs.com/xuwc/p/8523681.html + + 配置文件 + server.xml:主要的配置文件。 + web.xml:缺省的web app配置,WEB-INF/web.xml会覆盖该配置。 + context.xml:不清楚跟server.xml里面的context是否有关系。 + + server.xml配置 + server标签 + port:指定一个端口,这个端口负责监听关闭tomcat的请求。 + shutdown:指定向端口发送的命令字符串。 + + service标签 + name:指定service的名字。 + + Connector(表示客户端和service之间的连接)标签 + +port:指定服务器端要创建的端口号,并在这个端口监听来自客户端的请求。 + minProcessors:服务器启动时创建的处理请求的线程数。 + maxProcessors:最大可以创建的处理请求的线程数。 + enableLookups:如果为true,则可以通过调用request.getRemoteHost()进行DNS查询来得到远程客户端的实际主机名,若为false则不进行DNS查询,而是返回其ip地址。 + redirectPort:指定服务器正在处理http请求时收到了一个SSL传输请求后重定向的端口号。 + acceptCount:指定当所有可以使用的处理请求的线程数都被使用时,可以放到处理队列中的请求数,超过这个数的请求将不予处理。 + connectionTimeout:指定超时的时间数(以毫秒为单位)。 + + Engine(表示指定service中的请求处理机,接收和处理来自Connector的请求)标签 + defaultHost:指定缺省的处理请求的主机名,它至少与其中的一个host元素的name属性值是一样的。 + + Context(表示一个web应用程序,通常为WAR文件,关于WAR的具体信息见servlet规范)标签 + docBase:该web应用的文档基准目录(Document Base,也称为Context Root),或者是WAR文件的路径。可以使用绝对路径,也可以使用相对于context所属的Host的appBase路径。 + path:表示此web应用程序的url的前缀,这样请求的url为http://localhost:8080/path/****。 + ***\*reloadable:这个属性非常重要,如果为true,则tomcat会自动检测应用程序的/WEB-INF/lib和/WEB-INF/classes目录的变化,自动装载新的应用程序,我们可以在不重起tomcat的情况下改变应用程序。\**** + useNaming:如果希望Catalina为该web应用使能一个JNDI InitialContext对象,设为true。该InitialialContext符合J2EE平台的约定,缺省值为true。 + workDir:Context提供的临时目录的路径,用于servlet的临时读/写。利用javax.servlet.context.tempdir属性,servlet可以访问该目录。如果没有指定,使用$CATALINA_HOME/work下一个合适的目录。 + swallowOutput:如果该值为true,System.out和System.err的输出被重定向到web应用的logger。如果没有指定,缺省值为false + debug:与这个Engine关联的Logger记录的调试信息的详细程度。数字越大,输出越详细。如果没有指定,缺省为0。 + + host(表示一个虚拟主机)标签 + name:指定主机名。 + appBase:应用程序基本目录,即存放应用程序的目录。 + unpackWARs:如果为true,则tomcat会自动将WAR文件解压,否则不解压,直接从WAR文件中运行应用程序。 + + Logger(表示日志,调试和错误信息)标签 + className:指定logger使用的类名,此类必须实现org.apache.catalina.Logger接口。 + prefix:指定log文件的前缀。 + suffix:指定log文件的后缀。 + timestamp:如果为true,则log文件名中要加入时间,如下例:localhost_log.2001-10-04.txt。 + + Realm(表示存放用户名,密码及role的数据库)标签 + className:指定Realm使用的类名,此类必须实现org.apache.catalina.Realm接口。 + + Valve(功能与Logger差不多,其prefix和suffix属性解释和Logger 中的一样)标签 + className:指定Valve使用的类名,如用org.apache.catalina.valves.AccessLogValve类可以记录应用程序的访问信息。 + directory:指定log文件存放的位置。 + pattern:有两个值,common方式记录远程主机名或ip地址,用户名,日期,第一行请求的字符串,HTTP响应代码,发送的字节数。combined方式比common方式记录的值更多。 + +# ***\*tomcat的安全配置:\**** + +https://blog.51cto.com/8248183/2062343 + + + +首次安装完成后立即删除webapps下面的所有代码 +rm -rf /srv/apache-tomcat/webapps/* +注释或删除 tomcat-users.xml 所有用户权限,看上去如下: + +# ***\*cat conf/tomcat-users.xml\**** + + + + + + +2、隐藏tomcat版本 +01.首先找到这个jar包,$TOMCAT_HOME/lib/catalina.jar +02.解压catalina.jar之后按照路径\org\apache\catalina\util\ServerInfo.properties找到文件 +03.打开ServerInfo.properties文件修改如下:把server.number、server.built置空 +server.info=Apache Tomcat +server.number= +server.built= +04.重新打成jar包,重启tomcat。 +3、隐藏tomcat 的服务类型 +conf/server.xml文件中,为connector元素添加server=" +",注意不是空字符串,是空格组成的长度为1的字符串,或者输入其他的服务类型,这时候,在response header中就没有server的信息啦! +4、应用程序安全 +关闭war自动部署 unpackWARs="false" autoDeploy="false"。防止被植入木马等恶意程序 +5、修改服务监听端口 +一般公司的 Tomcat 都是放在内网的,因此我们针对 Tomcat 服务的监听地址都是内网地址。 +修改实例: + + + + + +## 手写Tomcat + +参考连接: https://blog.csdn.net/weixin_39033443/article/details/83901722、https://www.cnblogs.com/jyroy/p/10778760.html + +### 主要步骤 + +HTTP 协议基本解析 + +处理 GET 或者 POST 请求逻辑 + +启动容器(socket) + + + + + + + + + diff --git a/study-tomcat/readme.docx b/study-tomcat/readme.docx deleted file mode 100644 index f4eab1684..000000000 Binary files a/study-tomcat/readme.docx and /dev/null differ diff --git a/study-utils/pom.xml b/study-utils/pom.xml index 8463cec56..087bc8a7b 100644 --- a/study-utils/pom.xml +++ b/study-utils/pom.xml @@ -12,6 +12,8 @@ http://maven.apache.org UTF-8 + 1.8 + 1.8 diff --git a/study-vue-npm/pom.xml b/study-vue-npm/pom.xml index 5792e1b86..770071e1f 100644 --- a/study-vue-npm/pom.xml +++ b/study-vue-npm/pom.xml @@ -17,8 +17,8 @@ UTF-8 - 1.7 - 1.7 + 1.8 + 1.8 diff --git a/study-vue/README-vue-mac.md b/study-vue/README-vue-mac.md new file mode 100644 index 000000000..2a6c23443 --- /dev/null +++ b/study-vue/README-vue-mac.md @@ -0,0 +1,135 @@ +# study-vue-Mac # + + +Mac 搭建vue环境 + +https://www.jianshu.com/p/a056e41833a7 + + + +## 安装HomeBrew + +https://docs.brew.sh + + + +安装 + +```bash +/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" +``` + + + +验证 + +```undefined +brew -v +``` + + + +异常处理 + +```undefined +==> Homebrew has enabled anonymous aggregate formulae and cask analytics. +Read the analytics documentation (and how to opt-out) here: + https://docs.brew.sh/Analytics +No analytics data has been sent yet (nor will any be during this install run). + +==> Homebrew is run entirely by unpaid volunteers. Please consider donating: + https://github.com/Homebrew/brew#donations + +==> Next steps: +- Run these two commands in your terminal to add Homebrew to your PATH: + echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> /Users/bage/.zprofile + eval "$(/opt/homebrew/bin/brew shellenv)" +- Run brew help to get started +- Further documentation: + https://docs.brew.sh +``` + + + +补充安装 + +```undefined +echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> /Users/bage/.zprofile + +eval "$(/opt/homebrew/bin/brew shellenv)" +``` + + + +## 安装 node + +安装 + +```undefined +brew install nodejs +``` + +验证 + +```undefined +node -v +``` + + + + + +## 安装 vue + +安装 + +```undefined +npm install --global vue-cli + +npm install --global vue-cli@3.0.1 + +npm config set registry https://registry.npm.taobao.org + +``` + +验证 + +```undefined +vue -V +``` + +适当更新版本 + +```undefined +npm install -g npm@8.10.0 +``` + + + +## 安装 yarn + +安装 + +```undefined +brew install yarn + +yarn global add vue-cli + +``` + +设置淘宝镜像 + +``` +yarn config set registry http://registry.npm.taobao.org/ + + +``` + +验证 + +```undefined +yarn -V +``` + + + diff --git a/study-vue/pom.xml b/study-vue/pom.xml index 805e5e186..0c50307df 100644 --- a/study-vue/pom.xml +++ b/study-vue/pom.xml @@ -17,8 +17,8 @@ UTF-8 - 1.7 - 1.7 + 1.8 + 1.8 diff --git a/study-websocket/pom.xml b/study-websocket/pom.xml index a7a577479..913c3cd24 100644 --- a/study-websocket/pom.xml +++ b/study-websocket/pom.xml @@ -18,6 +18,8 @@ UTF-8 + 1.8 + 1.8 2.0.1.RELEASE diff --git a/study-xfyun-lfasr/pom.xml b/study-xfyun-lfasr/pom.xml index 238af84c6..29d775872 100644 --- a/study-xfyun-lfasr/pom.xml +++ b/study-xfyun-lfasr/pom.xml @@ -17,8 +17,8 @@ UTF-8 - 1.7 - 1.7 + 1.8 + 1.8 diff --git a/study-xfyun-lfasr/src/main/java/com/bage/study/xfyun/lfasr/JsonParser.java b/study-xfyun-lfasr/src/main/java/com/bage/study/xfyun/lfasr/JsonParser.java index 40c826aa0..2f568914b 100644 --- a/study-xfyun-lfasr/src/main/java/com/bage/study/xfyun/lfasr/JsonParser.java +++ b/study-xfyun-lfasr/src/main/java/com/bage/study/xfyun/lfasr/JsonParser.java @@ -1,22 +1,22 @@ -package com.bage.study.xfyun.lfasr; - -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONArray; -import com.alibaba.fastjson.JSONObject; - -public class JsonParser { - - public static String parseMessage(String data) { - JSONArray jsonArray = JSON.parseArray(data); - StringBuilder sb = new StringBuilder(); - JSONObject item = null; - for (int i = 0; i < jsonArray.size(); i++) { - item = jsonArray.getJSONObject(i); - if(item != null) { - sb.append(item.getString("onebest")); - } - } - return sb.toString(); - } - -} +//package com.bage.study.xfyun.lfasr; +// +//import com.alibaba.fastjson.JSON; +//import com.alibaba.fastjson.JSONArray; +//import com.alibaba.fastjson.JSONObject; +// +//public class JsonParser { +// +// public static String parseMessage(String data) { +// JSONArray jsonArray = JSON.parseArray(data); +// StringBuilder sb = new StringBuilder(); +// JSONObject item = null; +// for (int i = 0; i < jsonArray.size(); i++) { +// item = jsonArray.getJSONObject(i); +// if(item != null) { +// sb.append(item.getString("onebest")); +// } +// } +// return sb.toString(); +// } +// +//} diff --git a/study-xfyun-lfasr/src/main/java/com/bage/study/xfyun/lfasr/LfasrSDKDemo.java b/study-xfyun-lfasr/src/main/java/com/bage/study/xfyun/lfasr/LfasrSDKDemo.java index ad5db7779..96be95f91 100644 --- a/study-xfyun-lfasr/src/main/java/com/bage/study/xfyun/lfasr/LfasrSDKDemo.java +++ b/study-xfyun-lfasr/src/main/java/com/bage/study/xfyun/lfasr/LfasrSDKDemo.java @@ -1,133 +1,133 @@ -package com.bage.study.xfyun.lfasr; - -import com.alibaba.fastjson.JSON; -import com.iflytek.msp.cpdb.lfasr.client.LfasrClientImp; -import com.iflytek.msp.cpdb.lfasr.exception.LfasrException; -import com.iflytek.msp.cpdb.lfasr.model.LfasrType; -import com.iflytek.msp.cpdb.lfasr.model.Message; -import com.iflytek.msp.cpdb.lfasr.model.ProgressStatus; - -import java.io.File; -import java.util.HashMap; - -/** - * 非实时转写SDK调用demo - * 此demo只是一个简单的调用示例, 不适合用到实际生产环境中 - * - * @author white - * - */ -public class LfasrSDKDemo { - - // 原始音频存放地址 - private static final String local_file = "E:\\workspace\\IdeaProjects\\study\\study\\study-xfyun-lfasr\\target\\classes\\audio\\lfasr.wav"; - - /* - * 转写类型选择:标准版和电话版(旧版本, 不建议使用)分别为: - * LfasrType.LFASR_STANDARD_RECORDED_AUDIO 和 LfasrType.LFASR_TELEPHONY_RECORDED_AUDIO - * */ - private static final LfasrType type = LfasrType.LFASR_STANDARD_RECORDED_AUDIO; - - // 等待时长(毫秒) - private static int sleepMillis = 500; - - public static void main(String[] args) { - // 初始化LFASRClient实例 - LfasrClientImp lc = null; - try { - lc = LfasrClientImp.initLfasrClient(); - } catch (LfasrException e) { - // 初始化异常,解析异常描述信息 - Message initMsg = JSON.parseObject(e.getMessage(), Message.class); - System.out.println("ecode=" + initMsg.getErr_no()); - System.out.println("failed=" + initMsg.getFailed()); - } - - // 获取上传任务ID - String task_id = ""; - HashMap params = new HashMap(); - params.put("has_participle", "true"); - //合并后标准版开启电话版功能 - //params.put("has_seperate", "true"); - try { - // 上传音频文件 - Message uploadMsg = lc.lfasrUpload(local_file, type, params); - - // 判断返回值 - int ok = uploadMsg.getOk(); - if (ok == 0) { - // 创建任务成功 - task_id = uploadMsg.getData(); - System.out.println("task_id=" + task_id); - } else { - // 创建任务失败-服务端异常 - System.out.println("ecode=" + uploadMsg.getErr_no()); - System.out.println("failed=" + uploadMsg.getFailed()); - } - } catch (LfasrException e) { - // 上传异常,解析异常描述信息 - Message uploadMsg = JSON.parseObject(e.getMessage(), Message.class); - System.out.println("ecode=" + uploadMsg.getErr_no()); - System.out.println("failed=" + uploadMsg.getFailed()); - } - - // 循环等待音频处理结果 - while (true) { - try { - // 等待sleepMillis在获取任务进度 - Thread.sleep(sleepMillis); - System.out.println("waiting ..."); - } catch (InterruptedException e) { - e.printStackTrace(); - } - try { - // 获取处理进度 - Message progressMsg = lc.lfasrGetProgress(task_id); - - // 如果返回状态不等于0,则任务失败 - if (progressMsg.getOk() != 0) { - System.out.println("task was fail. task_id:" + task_id); - System.out.println("ecode=" + progressMsg.getErr_no()); - System.out.println("failed=" + progressMsg.getFailed()); - - return; - } else { - ProgressStatus progressStatus = JSON.parseObject(progressMsg.getData(), ProgressStatus.class); - if (progressStatus.getStatus() == 9) { - // 处理完成 - System.out.println("task was completed. task_id:" + task_id); - break; - } else { - // 未处理完成 - System.out.println("task is incomplete. task_id:" + task_id + ", status:" + progressStatus.getDesc()); - continue; - } - } - } catch (LfasrException e) { - // 获取进度异常处理,根据返回信息排查问题后,再次进行获取 - Message progressMsg = JSON.parseObject(e.getMessage(), Message.class); - System.out.println("ecode=" + progressMsg.getErr_no()); - System.out.println("failed=" + progressMsg.getFailed()); - } - } - - // 获取任务结果 - try { - Message resultMsg = lc.lfasrGetResult(task_id); - // 如果返回状态等于0,则获取任务结果成功 - if (resultMsg.getOk() == 0) { - // 打印转写结果 - System.out.println(JsonParser.parseMessage(resultMsg.getData())); - } else { - // 获取任务结果失败 - System.out.println("ecode=" + resultMsg.getErr_no()); - System.out.println("failed=" + resultMsg.getFailed()); - } - } catch (LfasrException e) { - // 获取结果异常处理,解析异常描述信息 - Message resultMsg = JSON.parseObject(e.getMessage(), Message.class); - System.out.println("ecode=" + resultMsg.getErr_no()); - System.out.println("failed=" + resultMsg.getFailed()); - } - } -} \ No newline at end of file +//package com.bage.study.xfyun.lfasr; +// +//import com.alibaba.fastjson.JSON; +//import com.iflytek.msp.cpdb.lfasr.client.LfasrClientImp; +//import com.iflytek.msp.cpdb.lfasr.exception.LfasrException; +//import com.iflytek.msp.cpdb.lfasr.model.LfasrType; +//import com.iflytek.msp.cpdb.lfasr.model.Message; +//import com.iflytek.msp.cpdb.lfasr.model.ProgressStatus; +// +//import java.io.File; +//import java.util.HashMap; +// +///** +// * 非实时转写SDK调用demo +// * 此demo只是一个简单的调用示例, 不适合用到实际生产环境中 +// * +// * @author white +// * +// */ +//public class LfasrSDKDemo { +// +// // 原始音频存放地址 +// private static final String local_file = "E:\\workspace\\IdeaProjects\\study\\study\\study-xfyun-lfasr\\target\\classes\\audio\\lfasr.wav"; +// +// /* +// * 转写类型选择:标准版和电话版(旧版本, 不建议使用)分别为: +// * LfasrType.LFASR_STANDARD_RECORDED_AUDIO 和 LfasrType.LFASR_TELEPHONY_RECORDED_AUDIO +// * */ +// private static final LfasrType type = LfasrType.LFASR_STANDARD_RECORDED_AUDIO; +// +// // 等待时长(毫秒) +// private static int sleepMillis = 500; +// +// public static void main(String[] args) { +// // 初始化LFASRClient实例 +// LfasrClientImp lc = null; +// try { +// lc = LfasrClientImp.initLfasrClient(); +// } catch (LfasrException e) { +// // 初始化异常,解析异常描述信息 +// Message initMsg = JSON.parseObject(e.getMessage(), Message.class); +// System.out.println("ecode=" + initMsg.getErr_no()); +// System.out.println("failed=" + initMsg.getFailed()); +// } +// +// // 获取上传任务ID +// String task_id = ""; +// HashMap params = new HashMap(); +// params.put("has_participle", "true"); +// //合并后标准版开启电话版功能 +// //params.put("has_seperate", "true"); +// try { +// // 上传音频文件 +// Message uploadMsg = lc.lfasrUpload(local_file, type, params); +// +// // 判断返回值 +// int ok = uploadMsg.getOk(); +// if (ok == 0) { +// // 创建任务成功 +// task_id = uploadMsg.getData(); +// System.out.println("task_id=" + task_id); +// } else { +// // 创建任务失败-服务端异常 +// System.out.println("ecode=" + uploadMsg.getErr_no()); +// System.out.println("failed=" + uploadMsg.getFailed()); +// } +// } catch (LfasrException e) { +// // 上传异常,解析异常描述信息 +// Message uploadMsg = JSON.parseObject(e.getMessage(), Message.class); +// System.out.println("ecode=" + uploadMsg.getErr_no()); +// System.out.println("failed=" + uploadMsg.getFailed()); +// } +// +// // 循环等待音频处理结果 +// while (true) { +// try { +// // 等待sleepMillis在获取任务进度 +// Thread.sleep(sleepMillis); +// System.out.println("waiting ..."); +// } catch (InterruptedException e) { +// e.printStackTrace(); +// } +// try { +// // 获取处理进度 +// Message progressMsg = lc.lfasrGetProgress(task_id); +// +// // 如果返回状态不等于0,则任务失败 +// if (progressMsg.getOk() != 0) { +// System.out.println("task was fail. task_id:" + task_id); +// System.out.println("ecode=" + progressMsg.getErr_no()); +// System.out.println("failed=" + progressMsg.getFailed()); +// +// return; +// } else { +// ProgressStatus progressStatus = JSON.parseObject(progressMsg.getData(), ProgressStatus.class); +// if (progressStatus.getStatus() == 9) { +// // 处理完成 +// System.out.println("task was completed. task_id:" + task_id); +// break; +// } else { +// // 未处理完成 +// System.out.println("task is incomplete. task_id:" + task_id + ", status:" + progressStatus.getDesc()); +// continue; +// } +// } +// } catch (LfasrException e) { +// // 获取进度异常处理,根据返回信息排查问题后,再次进行获取 +// Message progressMsg = JSON.parseObject(e.getMessage(), Message.class); +// System.out.println("ecode=" + progressMsg.getErr_no()); +// System.out.println("failed=" + progressMsg.getFailed()); +// } +// } +// +// // 获取任务结果 +// try { +// Message resultMsg = lc.lfasrGetResult(task_id); +// // 如果返回状态等于0,则获取任务结果成功 +// if (resultMsg.getOk() == 0) { +// // 打印转写结果 +// System.out.println(JsonParser.parseMessage(resultMsg.getData())); +// } else { +// // 获取任务结果失败 +// System.out.println("ecode=" + resultMsg.getErr_no()); +// System.out.println("failed=" + resultMsg.getFailed()); +// } +// } catch (LfasrException e) { +// // 获取结果异常处理,解析异常描述信息 +// Message resultMsg = JSON.parseObject(e.getMessage(), Message.class); +// System.out.println("ecode=" + resultMsg.getErr_no()); +// System.out.println("failed=" + resultMsg.getFailed()); +// } +// } +//} \ No newline at end of file diff --git a/study-xxl-job/pom.xml b/study-xxl-job/pom.xml index 89d7e3abd..200a02a8d 100644 --- a/study-xxl-job/pom.xml +++ b/study-xxl-job/pom.xml @@ -69,7 +69,7 @@ org.projectlombok lombok - 1.16.18 + 1.18.20 provided diff --git a/study-zipkin/pom.xml b/study-zipkin/pom.xml index 66da46201..932aea4c3 100644 --- a/study-zipkin/pom.xml +++ b/study-zipkin/pom.xml @@ -17,8 +17,8 @@ UTF-8 - 1.7 - 1.7 + 1.8 + 1.8 2.0.1.RELEASE 5.6.10