diff --git a/.drone.yml b/.drone.yml
deleted file mode 100644
index 6a32103b..00000000
--- a/.drone.yml
+++ /dev/null
@@ -1,232 +0,0 @@
-kind: pipeline
-type: docker
-name: build
-
-steps:
- - name: restore-cache
- image: drillster/drone-volume-cache
- volumes:
- - name: blog-node-cache
- path: /cache
- settings:
- restore: true
- mount:
- - ./node_modules
-
- - name: install
- image: registry.dev.xkcoding.com/nodebuild:8.17.0-alpine3.11
- pull: if-not-exists
- commands:
- - npm install
-
- - name: rebuild-cache
- image: drillster/drone-volume-cache
- volumes:
- - name: blog-node-cache
- path: /cache
- settings:
- rebuild: true
- mount:
- - ./node_modules
-
- - name: build
- image: registry.dev.xkcoding.com/nodebuild:8.17.0-alpine3.11
- pull: if-not-exists
- commands:
- - echo "开始打包Hexo博客"
- - hexo clean && hexo g
- - echo "开始压缩Hexo博客"
- - gulp
- - ls -a public/
-
- - name: build-image-dev
- image: plugins/docker
- settings:
- registry: registry.dev.xkcoding.com
- repo: registry.dev.xkcoding.com/${DRONE_REPO}
- tags: ${DRONE_COMMIT_SHA:0:8}
-
- - name: deploy-to-dev
- image: appleboy/drone-ssh
- settings:
- host:
- from_secret: dev_host
- user:
- from_secret: dev_user
- password:
- from_secret: dev_password
- port: 10022
- command_timeout: 10m
- script:
- - echo "=======开始部署======="
- - cd /root/apps/myblog
- - echo "=======下线服务======="
- - docker compose down
- - echo "=======历史镜像版本备份======="
- - cp .env .env.bak
- - cat .env.bak
- - echo "=======删除历史镜像======="
- - docker rmi $(cat .env.bak | grep 'IMAGE=' | cut -d '=' -f 2)
- - echo "=======新镜像版本======="
- - echo IMAGE=registry.dev.xkcoding.com/${DRONE_REPO}:${DRONE_COMMIT_SHA:0:8} > .env
- - cat .env
- - echo "=======拉取新镜像======="
- - docker pull registry.dev.xkcoding.com/${DRONE_REPO}:${DRONE_COMMIT_SHA:0:8}
- - echo "=======启动服务======="
- - docker compose up -d
- - echo "=======部署成功======="
-
- - name: notification-success
- image: yakumioto/drone-serverchan:2.0.0
- settings:
- key:
- from_secret: sc_key
- text: "[CI通知]-[blog]✅"
- desp: "仓库:${DRONE_REPO} 分支:`${DRONE_COMMIT_BRANCH}`\n\n💬 状态:开发环境部署成功\n\n🌐 详情:[CI地址](${DRONE_BUILD_LINK})\n\n📄 预览:[blog](https://blog.dev.xkcoding.com)"
- when:
- status:
- - success
-
- - name: notification-failure
- image: yakumioto/drone-serverchan:2.0.0
- settings:
- key:
- from_secret: sc_key
- text: "[CI通知]-[blog]❌"
- desp: "仓库:${DRONE_REPO} 分支:`${DRONE_COMMIT_BRANCH}`\n\n💬 状态:开发环境部署失败\n\n🌐 详情:[CI地址](${DRONE_BUILD_LINK})\n\n📄 内容:[diff](${DRONE_COMMIT_LINK})"
- when:
- status:
- - failure
-
-trigger:
- event:
- - push
-
-volumes:
- - name: blog-node-cache
- host:
- path: /tmp/cache
-
----
-kind: pipeline
-type: docker
-name: release
-
-steps:
- - name: restore-cache
- image: drillster/drone-volume-cache
- volumes:
- - name: blog-node-cache
- path: /cache
- settings:
- restore: true
- mount:
- - ./node_modules
-
- - name: install
- image: registry.dev.xkcoding.com/nodebuild:8.17.0-alpine3.11
- pull: if-not-exists
- commands:
- - npm install
-
- - name: rebuild-cache
- image: drillster/drone-volume-cache
- volumes:
- - name: blog-node-cache
- path: /cache
- settings:
- rebuild: true
- mount:
- - ./node_modules
-
- - name: build
- image: registry.dev.xkcoding.com/nodebuild:8.17.0-alpine3.11
- pull: if-not-exists
- commands:
- - echo "开始打包Hexo博客"
- - hexo clean && hexo g
- - echo "开始压缩Hexo博客"
- - gulp
- - ls -a public/
-
- - name: build-image-prod
- image: plugins/docker
- settings:
- registry: registry.cn-hangzhou.aliyuncs.com
- repo: registry.cn-hangzhou.aliyuncs.com/xkcoding/myblog
- tags: ${DRONE_COMMIT_SHA:0:8}
- username:
- from_secret: aliyun_docker_user
- password:
- from_secret: aliyun_docker_password
-
- - name: deploy-to-prod
- image: appleboy/drone-ssh
- settings:
- host:
- from_secret: prod_host
- user:
- from_secret: prod_user
- password:
- from_secret: prod_password
- port: 10022
- command_timeout: 10m
- script:
- - echo "=======开始部署======="
- - cd /root/apps/myblog
- - echo "=======下线服务======="
- - docker compose down
- - echo "=======历史镜像版本备份======="
- - cp .env .env.bak
- - cat .env.bak
- - echo "=======删除历史镜像======="
- - docker rmi $(cat .env.bak | grep 'IMAGE=' | cut -d '=' -f 2)
- - echo "=======新镜像版本======="
- - echo IMAGE=registry.cn-hangzhou.aliyuncs.com/xkcoding/myblog:${DRONE_COMMIT_SHA:0:8} > .env
- - cat .env
- - echo "=======拉取新镜像======="
- - docker pull registry.cn-hangzhou.aliyuncs.com/xkcoding/myblog:${DRONE_COMMIT_SHA:0:8}
- - echo "=======启动服务======="
- - docker compose up -d
- - echo "=======部署成功======="
-
- - name: push-github
- image: appleboy/drone-git-push:0.2.2
- settings:
- branch: ${DRONE_BRANCH}
- remote: git@github.com:xkcoding/MyBlog.git
- ssh_key:
- from_secret: ssh_key
- force: true
- commit: true
- commit_message: ${DRONE_COMMIT_MESSAGE}
- author_name: ${DRONE_COMMIT_AUTHOR_NAME}
- author_email: ${DRONE_COMMIT_AUTHOR_EMAIL}
-
- - name: notification-success
- image: yakumioto/drone-serverchan:2.0.0
- settings:
- key:
- from_secret: sc_key
- text: "[CI通知]-[blog]✅"
- desp: "仓库:${DRONE_REPO} 分支:`${DRONE_COMMIT_BRANCH}`\n\n💬 状态:线上环境部署成功\n\n🌐 详情:[CI地址](${DRONE_BUILD_LINK})\n\n📄 预览:[blog](https://xkcoding.com)"
- when:
- status:
- - success
-
- - name: notification-failure
- image: yakumioto/drone-serverchan:2.0.0
- settings:
- key:
- from_secret: sc_key
- text: "[CI通知]-[blog]❌"
- desp: "仓库:${DRONE_REPO} 分支:`${DRONE_COMMIT_BRANCH}`\n\n💬 状态:线上环境部署失败\n\n🌐 详情:[CI地址](${DRONE_BUILD_LINK})\n\n📄 内容:[diff](${DRONE_COMMIT_LINK})"
- when:
- status:
- - failure
-
-trigger:
- event:
- - promote
- target:
- - release
\ No newline at end of file
diff --git a/.gitattributes b/.gitattributes
deleted file mode 100644
index 8b4ebcb9..00000000
--- a/.gitattributes
+++ /dev/null
@@ -1,2 +0,0 @@
-/.deploy_git
-/public
diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md
new file mode 100644
index 00000000..03fae148
--- /dev/null
+++ b/.github/CODE_OF_CONDUCT.md
@@ -0,0 +1,124 @@
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+We as members, contributors, and leaders pledge to make participation in our
+community a harassment-free experience for everyone, regardless of age, body
+size, visible or invisible disability, ethnicity, sex characteristics, gender
+identity and expression, level of experience, education, socio-economic status,
+nationality, personal appearance, race, religion, or sexual identity
+and orientation.
+
+We pledge to act and interact in ways that contribute to an open, welcoming,
+diverse, inclusive, and healthy community.
+
+## Our Standards
+
+Examples of behavior that contributes to a positive environment for our
+community include:
+
+- Demonstrating empathy and kindness toward other people
+- Being respectful of differing opinions, viewpoints, and experiences
+- Giving and gracefully accepting constructive feedback
+- Accepting responsibility and apologizing to those affected by our mistakes,
+ and learning from the experience
+- Focusing on what is best not just for us as individuals, but for the
+ overall community
+
+Examples of unacceptable behavior include:
+
+- The use of sexualized language or imagery, and sexual attention or
+ advances of any kind
+- Trolling, insulting or derogatory comments, and personal or political attacks
+- Public or private harassment
+- Publishing others' private information, such as a physical or email
+ address, without their explicit permission
+- Other conduct which could reasonably be considered inappropriate in a
+ professional setting
+
+## Enforcement Responsibilities
+
+Community leaders are responsible for clarifying and enforcing our standards of
+acceptable behavior and will take appropriate and fair corrective action in
+response to any behavior that they deem inappropriate, threatening, offensive,
+or harmful.
+
+Community leaders have the right and responsibility to remove, edit, or reject
+comments, commits, code, wiki edits, issues, and other contributions that are
+not aligned to this Code of Conduct, and will communicate reasons for moderation
+decisions when appropriate.
+
+## Scope
+
+This Code of Conduct applies within all community spaces, and also applies when
+an individual is officially representing the community in public spaces.
+Examples of representing our community include using an official e-mail address,
+posting via an official social media account, or acting as an appointed
+representative at an online or offline event.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported to the community leaders responsible for enforcement at [satnaingdev@gmail.com](satnaingdev@gmail.com).
+All complaints will be reviewed and investigated promptly and fairly.
+
+All community leaders are obligated to respect the privacy and security of the
+reporter of any incident.
+
+## Enforcement Guidelines
+
+Community leaders will follow these Community Impact Guidelines in determining
+the consequences for any action they deem in violation of this Code of Conduct:
+
+### 1. Correction
+
+**Community Impact**: Use of inappropriate language or other behavior deemed
+unprofessional or unwelcome in the community.
+
+**Consequence**: A private, written warning from community leaders, providing
+clarity around the nature of the violation and an explanation of why the
+behavior was inappropriate. A public apology may be requested.
+
+### 2. Warning
+
+**Community Impact**: A violation through a single incident or series
+of actions.
+
+**Consequence**: A warning with consequences for continued behavior. No
+interaction with the people involved, including unsolicited interaction with
+those enforcing the Code of Conduct, for a specified period of time. This
+includes avoiding interactions in community spaces as well as external channels
+like social media. Violating these terms may lead to a temporary or
+permanent ban.
+
+### 3. Temporary Ban
+
+**Community Impact**: A serious violation of community standards, including
+sustained inappropriate behavior.
+
+**Consequence**: A temporary ban from any sort of interaction or public
+communication with the community for a specified period of time. No public or
+private interaction with the people involved, including unsolicited interaction
+with those enforcing the Code of Conduct, is allowed during this period.
+Violating these terms may lead to a permanent ban.
+
+### 4. Permanent Ban
+
+**Community Impact**: Demonstrating a pattern of violation of community
+standards, including sustained inappropriate behavior, harassment of an
+individual, or aggression toward or disparagement of classes of individuals.
+
+**Consequence**: A permanent ban from any sort of public interaction within
+the community.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage],
+version 2.0, available at [this link](https://www.contributor-covenant.org/version/2/0/code_of_conduct.html).
+
+Community Impact Guidelines were inspired by [Mozilla's code of conduct
+enforcement ladder](https://github.com/mozilla/diversity).
+
+[homepage]: https://www.contributor-covenant.org
+
+For answers to common questions about this code of conduct, see the [FAQ](https://www.contributor-covenant.org/faq). [Translations](https://www.contributor-covenant.org/translations) are also available.
diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
new file mode 100644
index 00000000..dc8cfa08
--- /dev/null
+++ b/.github/CONTRIBUTING.md
@@ -0,0 +1,55 @@
+# How to contribute to AstroPaper
+
+Thank you for your interest in contributing to **AstroPaper**! We appreciate every contribution, whether you're fixing a typo, improving documentation, or adding a new feature.
+
+## Types of Contributions
+
+There are several ways to contribute to **AstroPaper**, and every contribution counts\_ whether it's a PR for a major feature or a small fix.
+
+You can also contribute by leaving review comments on PRs, adding ideas to existing GitHub Issues and Discussions, or helping others by answering questions in GitHub Discussions.
+
+Here’s a summary of the different ways you can contribute:
+
+- [Opening a new issue](#open-a-new-issue)
+- [Submitting PRs](#feature-requests)
+ - [Solving an existing issue](#solving-an-issue)
+ - [Making changes to a blog post](#making-changes-to-a-blog-post)
+- [Helping others by answering issues/discussions](#helping-with-github-issuesdiscussions)
+- [Reviewing existing PRs](#reviewing-existing-prs)
+- [Starting a discussion](#starting-a-discussion)
+
+## Open a new Issue
+
+If you find a bug or problem, first check whether a similar issue already exists. If you don’t find any open issue that addresses the bug/problem you’re facing, feel free to [open a new issue](https://github.com/satnaing/astro-paper/issues/new/choose).
+
+## Feature Requests
+
+If you have an idea for a new feature or enhancement that could improve AstroPaper, we’d love to hear it! Before submitting a new feature request, please:
+
+1. **Check existing discussions/issues**: Review the [Discussions](https://github.com/satnaing/astro-paper/discussions) or [Issues](https://github.com/satnaing/astro-paper/issues) to see if the feature has already been requested or discussed. You can contribute by adding your thoughts or upvoting existing requests.
+2. **Open a new issue**: If you don’t find an existing discussion, you can open a new issue using the [Feature Request Template](https://github.com/satnaing/astro-paper/issues/new?assignees=&labels=enhancement&projects=&template=%E2%9C%A8-feature-request.md&title=%5BFeature+Request%5D%3A+). Be as detailed as possible, describing the problem this feature would solve and how it would benefit AstroPaper users.
+3. **Discuss first**: If you’re unsure whether your idea is feasible or fits the project’s goals, feel free to [start a GitHub Discussion](https://github.com/satnaing/astro-paper/discussions/new/choose) to gather feedback from the community.
+
+## Making PRs (Pull Requests)
+
+### Solving an Issue
+
+Browse through the existing issues to find one that interests you. You can use labels to filter the issues. See the [Label](https://github.com/satnaing/astro-paper/labels) section for more information.
+
+### Making Changes to a Blog Post
+
+For small changes like typos, syntax fixes, or broken links, click the "Suggest Changes" link below the title of any blog post. This will take you to the .md file, where you can make your changes and submit a pull request for review. For more significant changes to a blog post, it’s recommended to open a new issue or discussion first.
+
+## Helping with GitHub Issues/Discussions
+
+GitHub Discussions and Issues are great places to help others. Whether you're a long-time user of AstroPaper or just have experience with a specific problem, we encourage you to answer questions or solve issues when possible.
+
+## Reviewing Existing PRs
+
+You can help by reviewing and providing feedback on open PRs. Different perspectives can be very helpful.
+
+Since AstroPaper doesn’t currently have automated testing, it’s especially useful if you can do manual testing on open PRs and provide feedback.
+
+## Starting a Discussion
+
+If you’re unsure whether your issue warrants a fix or if you just want to share ideas and get feedback, feel free to [start a GitHub discussion](https://github.com/satnaing/astro-paper/discussions/new/choose). It’s a great way to engage with the community.
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 00000000..accbfedf
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,2 @@
+github: [satnaing]
+buy_me_a_coffee: satnaing
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 00000000..ecfa1bcb
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,5 @@
+blank_issues_enabled: false
+contact_links:
+ - name: AstroPaper Discussions
+ url: https://github.com/satnaing/astro-paper/discussions
+ about: Please ask and answer questions here.
diff --git "a/.github/ISSUE_TEMPLATE/\342\234\250-feature-request.md" "b/.github/ISSUE_TEMPLATE/\342\234\250-feature-request.md"
new file mode 100644
index 00000000..6d1d669d
--- /dev/null
+++ "b/.github/ISSUE_TEMPLATE/\342\234\250-feature-request.md"
@@ -0,0 +1,19 @@
+---
+name: "✨ Feature Request"
+about: Suggest an idea for improving AstroPaper
+title: "[Feature Request]: "
+labels: enhancement
+assignees: ""
+---
+
+**Is your feature request related to a problem? Please describe.**
+A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+
+**Describe the solution you'd like**
+A clear and concise description of what you want to happen.
+
+**Describe alternatives you've considered**
+A clear and concise description of any alternative solutions or features you've considered.
+
+**Additional context**
+Add any other context or screenshots about the feature request here.
diff --git "a/.github/ISSUE_TEMPLATE/\360\237\220\236-bug-report.md" "b/.github/ISSUE_TEMPLATE/\360\237\220\236-bug-report.md"
new file mode 100644
index 00000000..c949bdd8
--- /dev/null
+++ "b/.github/ISSUE_TEMPLATE/\360\237\220\236-bug-report.md"
@@ -0,0 +1,27 @@
+---
+name: "\U0001F41E Bug report"
+about: Report a bug or unexpected behavior in AstroPaper
+title: "[BUG]: "
+labels: bug
+assignees: ""
+---
+
+**Describe the bug**
+A clear and concise description of what the bug is.
+
+**To Reproduce**
+Steps to reproduce the behavior:
+
+1. Go to '...'
+2. Click on '....'
+3. Scroll down to '....'
+4. See error
+
+**Expected behavior**
+A clear and concise description of what you expected to happen.
+
+**Screenshots**
+If applicable, add screenshots to help explain your problem.
+
+**Additional context**
+Add any other context about the problem here.
diff --git "a/.github/ISSUE_TEMPLATE/\360\237\223\235-documentation-improvement.md" "b/.github/ISSUE_TEMPLATE/\360\237\223\235-documentation-improvement.md"
new file mode 100644
index 00000000..81cdc7ad
--- /dev/null
+++ "b/.github/ISSUE_TEMPLATE/\360\237\223\235-documentation-improvement.md"
@@ -0,0 +1,16 @@
+---
+name: "\U0001F4DD Documentation Improvement"
+about: Propose updates or improvements to the documentation/blog posts
+title: "[Docs]: "
+labels: documentation
+assignees: ""
+---
+
+**Describe the Issue**
+A clear and concise description of the documentation issue or improvement.
+
+**Proposed Changes**
+Describe what changes should be made and why they would improve the documentation.
+
+**Additional Context**
+Add any other context or screenshots about the documentation request here.
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 00000000..8bf5badc
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,30 @@
+## Description
+
+
+
+## Types of changes
+
+
+
+- [ ] Bug Fix (non-breaking change which fixes an issue)
+- [ ] New Feature (non-breaking change which adds functionality)
+- [ ] Documentation Update (if none of the other choices apply)
+- [ ] Others (any other types not listed above)
+
+## Checklist
+
+
+
+- [ ] I have read the [Contributing Guide](https://github.com/satnaing/astro-paper/blob/main/.github/CONTRIBUTING.md)
+- [ ] I have added the necessary documentation (if appropriate)
+- [ ] Breaking Change (fix or feature that would cause existing functionality to not work as expected)
+
+## Further comments
+
+
+
+## Related Issue
+
+
+
+Closes: #
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 00000000..7baede36
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,48 @@
+name: CI
+
+permissions:
+ contents: read
+
+on:
+ pull_request:
+ types:
+ - opened
+ - edited
+ - synchronize
+ - reopened
+ workflow_call:
+
+jobs:
+ build:
+ name: Code standards & build
+ runs-on: ubuntu-latest
+ timeout-minutes: 3
+
+ strategy:
+ matrix:
+ node-version: [20]
+
+ steps:
+ - name: "☁️ Checkout repository"
+ uses: actions/checkout@v4
+
+ - name: "📦 Install pnpm"
+ uses: pnpm/action-setup@v4
+
+ - name: Use Node.js ${{ matrix.node-version }}
+ uses: actions/setup-node@v4
+ with:
+ node-version: ${{ matrix.node-version }}
+ cache: "pnpm"
+
+ - name: "📦 Install dependencies"
+ run: pnpm install
+
+ - name: "🔎 Lint code"
+ run: pnpm run lint
+
+ - name: "📝 Checking code format"
+ run: pnpm run format:check
+
+ - name: "🚀 Build the project"
+ run: pnpm run build
diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
new file mode 100644
index 00000000..ca56059f
--- /dev/null
+++ b/.github/workflows/deploy.yml
@@ -0,0 +1,48 @@
+name: Deploy to GitHub Pages
+
+on:
+ push:
+ branches: [master]
+ workflow_dispatch:
+
+permissions:
+ contents: read
+ pages: write
+ id-token: write
+
+concurrency:
+ group: "pages"
+ cancel-in-progress: false
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Setup pnpm
+ uses: pnpm/action-setup@v4
+
+ - name: Setup Node
+ uses: actions/setup-node@v4
+ with:
+ node-version: 20
+ cache: pnpm
+
+ - name: Install dependencies
+ run: pnpm install
+
+ - name: Build with Astro
+ uses: withastro/action@v3
+
+ deploy:
+ needs: build
+ runs-on: ubuntu-latest
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
+ steps:
+ - name: Deploy to GitHub Pages
+ id: deployment
+ uses: actions/deploy-pages@v4
diff --git a/.gitignore b/.gitignore
index 3d126d0e..0866d468 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,8 +1,37 @@
-.deploy_git
+# build output
+dist/
+
+# generated types
+.astro/
+
+# dependencies
+node_modules/
+
+# logs
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+
+# environment variables
+.env
+.env.production
+
+# macOS-specific files
.DS_Store
-node_modules
-debug.log
-public
-*.lnk
+
+# jetbrains setting folder
+.idea/
+
+# pagefind
+public/pagefind
+
+# hexo legacy (backup)
+.deploy_git
db.json
-.idea/
\ No newline at end of file
+*.lnk
+.claude/
+hexo-backup/
+.history/
+.specstory/
+.cursorindexingignore
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
deleted file mode 100644
index 7b8f8ff8..00000000
--- a/.gitlab-ci.yml
+++ /dev/null
@@ -1,98 +0,0 @@
-variables:
- GIT_SSL_NO_VERIFY: "true"
- DOCKER_REGION: "registry.cn-hangzhou.aliyuncs.com"
- DOCKER_NAMESPACE: "xkcoding"
- APP_NAME: "myblog"
- BUILD_IMAGE: "$DOCKER_REGION/$DOCKER_NAMESPACE/nodebuild:8.17.0-alpine3.11"
- IMAGE_NAME: "$DOCKER_REGION/$DOCKER_NAMESPACE/$APP_NAME:$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA"
- DOCKER_FILE_PATH: "./Dockerfile"
- APP_DOMAIN: "xkcoding.com"
- CONTAINER_PORT: 80
-
-stages:
- - 下载依赖
- - 编译构建
- - 打包镜像
- - 部署服务
- - 服务下线
-
-cache:
- key: ${CI_COMMIT_REF_SLUG}
- paths:
- - public/
- - node_modules/
-
-下载依赖:
- stage: 下载依赖
- image: ${BUILD_IMAGE}
- retry: 2
- tags:
- - docker
- script:
- - ls -a
- - npm install
- - ls -a
- rules:
- - changes:
- - package.json
-
-编译构建:
- stage: 编译构建
- image: ${BUILD_IMAGE}
- retry: 2
- tags:
- - docker
- script:
- - ls -a
- - echo "开始打包Hexo博客"
- - hexo clean && hexo g
- - echo "开始压缩Hexo博客"
- - gulp
- - ls -a public/
-
-打包镜像:
- stage: 打包镜像
- image: docker:latest
- services:
- - name: docker:dind
- tags:
- - docker
- script:
- - ls -a
- - docker login -u ${DOCKER_USERNAME} -p ${DOCKER_PASSWORD} ${DOCKER_REGION}
- - docker build -t ${IMAGE_NAME} -f ${DOCKER_FILE_PATH} .
- - docker push ${IMAGE_NAME}
- - docker rmi ${IMAGE_NAME}
-
-部署服务:
- stage: 部署服务
- tags:
- - deploy
- script:
- - ls -a
- - sed -i "s#__IMAGE_NAME__#${IMAGE_NAME}#g" ${APP_NAME}.yml
- - sed -i "s#__APP_NAME__#${APP_NAME}#g" ${APP_NAME}.yml
- - sed -i "s#__APP_DOMAIN__#${APP_DOMAIN}#g" ${APP_NAME}.yml
- - sed -i "s#__CONTAINER_PORT__#${CONTAINER_PORT}#g" ${APP_NAME}.yml
- - cat ${APP_NAME}.yml
- - docker login -u ${DOCKER_USERNAME} -p ${DOCKER_PASSWORD} ${DOCKER_REGION}
- - docker stack deploy -c ${APP_NAME}.yml ${APP_NAME}
- cache:
- policy: pull
-
-服务下线:
- stage: 服务下线
- tags:
- - deploy
- script:
- - ls -a
- - sed -i "s#__IMAGE_NAME__#${IMAGE_NAME}#g" ${APP_NAME}.yml
- - sed -i "s#__APP_NAME__#${APP_NAME}#g" ${APP_NAME}.yml
- - sed -i "s#__APP_DOMAIN__#${APP_DOMAIN}#g" ${APP_NAME}.yml
- - sed -i "s#__CONTAINER_PORT__#${CONTAINER_PORT}#g" ${APP_NAME}.yml
- - cat ${APP_NAME}.yml
- - docker login -u ${DOCKER_USERNAME} -p ${DOCKER_PASSWORD} ${DOCKER_REGION}
- - docker stack rm ${APP_NAME}
- when: manual
- cache:
- policy: pull
diff --git a/.husky/pre-commit b/.husky/pre-commit
new file mode 100644
index 00000000..5ee7abd8
--- /dev/null
+++ b/.husky/pre-commit
@@ -0,0 +1 @@
+pnpm exec lint-staged
diff --git a/.jenkins/Jenkinsfile b/.jenkins/Jenkinsfile
deleted file mode 100644
index 404e0159..00000000
--- a/.jenkins/Jenkinsfile
+++ /dev/null
@@ -1,138 +0,0 @@
-#!/usr/bin/env groovy Jenkinsfile
-pipeline {
- agent any
- environment {
- CONFIG_FILE = "_config.yml"
- BLOG_DIR = "/data/xkcoding.com"
- GIT_REPO = "https://github.com/xkcoding/MyBlog.git"
- USERMAIL = "237497819@qq.com"
- }
-
- stages {
- stage("获取/更新代码代码") {
- steps {
- echo "Git仓库代码: ${GIT_REPO}"
- echo "校验是否已存在代码"
- script {
- if (fileExists(file:"${CONFIG_FILE}")) {
- echo "项目存在,准备更新代码"
- sh "git pull origin master"
- }else{
- echo "项目不存在,准备拉取代码"
- git 'https://github.com/xkcoding/MyBlog.git'
- }
- }
- }
- }
-
- stage("下载依赖") {
- steps {
- echo "开始下载NPM依赖"
- sh "npm install"
- }
- }
-
- stage("打包/压缩博客") {
- steps {
- echo "开始打包Hexo博客"
- sh "hexo clean && hexo g"
- echo "开始压缩Hexo博客"
- sh "export PATH=/apps/node-v8.0.0-linux-x64/bin:$PATH && gulp"
- }
- }
-
- stage("部署") {
- steps {
- echo "删除旧目录"
- sh "rm -rf ${BLOG_DIR}"
- echo "部署静态页面到nginx目录"
- sh "mv public ${BLOG_DIR}"
- echo "删除依赖目录"
- sh "rm -rf node_modules"
- }
- }
-
- stage("上线") {
- steps {
- echo "重启Nginx"
- sh "nginx -s reload"
- }
- }
- }
-
- post {
- always {
- emailext (
- subject: '''构建通知:${JOB_NAME} [${BUILD_NUMBER}] ${BUILD_STATUS}''',
- body: '''
-
-
-
-
- ${JOB_NAME}-第${BUILD_NUMBER}次构建日志
-
-
-
-
- (本邮件是程序自动下发的,请勿回复!)
-
-
-
- 构建结果 - ${BUILD_STATUS}
-
-
-
-
- 构建信息
-
-
-
-
-
-
-
-
- Changes Since Last Successful Build:
-
-
-
-
- ${CHANGES_SINCE_LAST_SUCCESS,reverse=true, format="Changes for Build #%n: %c ",showPaths=true,changesFormat="[%a] %m ",pathFormat=" %p"}
-
-
-
- Test Informations
-
-
-
- Total:${TEST_COUNTS,var="total"},Pass:${TEST_COUNTS,var="pass"},Failed:${TEST_COUNTS,var="fail"},Skiped:${TEST_COUNTS,var="skip"}
-
-
-
- 构建日志 (最后 100行):
-
-
-
-
-
-
-
-
-
- ''',
- to: "${USERMAIL}",
- recipientProviders: [[$class: 'DevelopersRecipientProvider']]
- )
- }
- }
-}
\ No newline at end of file
diff --git a/.npmignore b/.npmignore
deleted file mode 100644
index 063b0e4c..00000000
--- a/.npmignore
+++ /dev/null
@@ -1,7 +0,0 @@
-.DS_Store
-Thumbs.db
-db.json
-*.log
-node_modules/
-public/
-.deploy*/
\ No newline at end of file
diff --git a/.prettierignore b/.prettierignore
new file mode 100644
index 00000000..d223e721
--- /dev/null
+++ b/.prettierignore
@@ -0,0 +1,14 @@
+# Ignore everything
+/*
+
+# Except these files & folders
+!/src
+!/public
+!/.github
+!tsconfig.json
+!astro.config.ts
+!.prettierrc.mjs
+!package.json
+!.prettierrc
+!eslint.config.js
+!README.md
\ No newline at end of file
diff --git a/.prettierrc.mjs b/.prettierrc.mjs
new file mode 100644
index 00000000..5f2d7bdf
--- /dev/null
+++ b/.prettierrc.mjs
@@ -0,0 +1,22 @@
+/** @type {import("prettier").Config} */
+export default {
+ arrowParens: "avoid",
+ semi: true,
+ tabWidth: 2,
+ printWidth: 80,
+ singleQuote: false,
+ jsxSingleQuote: false,
+ trailingComma: "es5",
+ bracketSpacing: true,
+ endOfLine: "lf",
+ plugins: ["prettier-plugin-astro", "prettier-plugin-tailwindcss"],
+ tailwindStylesheet: "./src/styles/global.css",
+ overrides: [
+ {
+ files: "*.astro",
+ options: {
+ parser: "astro",
+ },
+ },
+ ],
+};
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 00000000..e9bbf4da
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,4 @@
+{
+ "workbench.activityBar.location": "default",
+ "workbench.activityBar.orientation": "horizontal"
+}
\ No newline at end of file
diff --git a/AGENTS.md b/AGENTS.md
new file mode 100644
index 00000000..06696994
--- /dev/null
+++ b/AGENTS.md
@@ -0,0 +1,18 @@
+
+# OpenSpec Instructions
+
+These instructions are for AI assistants working in this project.
+
+Always open `@/openspec/AGENTS.md` when the request:
+- Mentions planning or proposals (words like proposal, spec, change, plan)
+- Introduces new capabilities, breaking changes, architecture shifts, or big performance/security work
+- Sounds ambiguous and you need the authoritative spec before coding
+
+Use `@/openspec/AGENTS.md` to learn:
+- How to create and apply change proposals
+- Spec format and conventions
+- Project structure and guidelines
+
+Keep this managed block so 'openspec update' can refresh the instructions.
+
+
\ No newline at end of file
diff --git a/CLAUDE.md b/CLAUDE.md
new file mode 100644
index 00000000..06696994
--- /dev/null
+++ b/CLAUDE.md
@@ -0,0 +1,18 @@
+
+# OpenSpec Instructions
+
+These instructions are for AI assistants working in this project.
+
+Always open `@/openspec/AGENTS.md` when the request:
+- Mentions planning or proposals (words like proposal, spec, change, plan)
+- Introduces new capabilities, breaking changes, architecture shifts, or big performance/security work
+- Sounds ambiguous and you need the authoritative spec before coding
+
+Use `@/openspec/AGENTS.md` to learn:
+- How to create and apply change proposals
+- Spec format and conventions
+- Project structure and guidelines
+
+Keep this managed block so 'openspec update' can refresh the instructions.
+
+
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
deleted file mode 100644
index 6ad2da2a..00000000
--- a/Dockerfile
+++ /dev/null
@@ -1,5 +0,0 @@
-FROM nginx:1.18.0
-LABEL maintainer="xkcoding <237497819@qq.com>"
-
-COPY public/ /usr/share/nginx/html
-COPY default.conf /etc/nginx/conf.d/default.conf
\ No newline at end of file
diff --git a/themes/next/source/lib/bookmark/LICENSE b/LICENSE
similarity index 96%
rename from themes/next/source/lib/bookmark/LICENSE
rename to LICENSE
index d3a06b99..fa7c7912 100644
--- a/themes/next/source/lib/bookmark/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2018 NexT [Reloaded]
+Copyright (c) 2023 Sat Naing
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
index 2919215b..6e38e146 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,46 @@
-xkcoding的代码成长日记
\ No newline at end of file
+# CodingDiary
+
+xkcoding 的代码成长日记 - 基于 [Astro](https://astro.build/) + [AstroPaper](https://github.com/satnaing/astro-paper) 主题构建。
+
+## 技术栈
+
+- **框架**: Astro 5.x
+- **主题**: AstroPaper
+- **搜索**: Pagefind
+- **部署**: GitHub Pages + GitHub Actions
+
+## 本地开发
+
+```bash
+# 安装依赖
+pnpm install
+
+# 启动开发服务器
+pnpm run dev
+
+# 构建
+pnpm run build
+
+# 预览构建结果
+pnpm run preview
+```
+
+## 目录结构
+
+```
+/
+├── public/ # 静态资源
+├── src/
+│ ├── assets/ # 图片等资源
+│ ├── components/ # Astro 组件
+│ ├── data/blog/ # 博客文章 (Markdown)
+│ ├── layouts/ # 布局组件
+│ ├── pages/ # 页面路由
+│ └── config.ts # 站点配置
+├── astro.config.ts # Astro 配置
+└── package.json
+```
+
+## License
+
+MIT
diff --git a/_config.yml b/_config.yml
deleted file mode 100644
index c7f1dd23..00000000
--- a/_config.yml
+++ /dev/null
@@ -1,145 +0,0 @@
-# Hexo Configuration
-## Docs: https://hexo.io/docs/configuration.html
-## Source: https://github.com/hexojs/hexo/
-
-# Site
-title: CodingDiary
-subtitle: 代码日记
-description: xkcoding的代码成长日记
-author: xkcoding
-language: zh-CN
-timezone: Asia/Shanghai
-
-# RSS
-feed:
- type: atom
- path: atom.xml
- limit: 20
- hub:
- content:
-
-#search
-search:
- path: search.xml
- field: post
- format: html
- limit: 10000
-
-algolia:
- applicationID: 'ZV4RFZVZ58'
- apiKey: 'ad1e0a485cd7b9e16a583282fc2843c6'
- adminApiKey: '20fee041cebabaa5e9ef6727a101eb4a'
- indexName: 'xkcoding'
- chunkSize: 5000
- fields:
- - title
- - slug
- - path
- - content:strip
-
-# URL
-## If your site is put in a subdirectory, set url as 'http://yoursite.com/child' and root as '/child/'
-url: https://xkcoding.com/
-root: /
-permalink: :year/:month/:day/:title.html
-permalink_defaults:
-
-# Directory
-source_dir: source
-public_dir: public
-tag_dir: tags
-archive_dir: archives
-category_dir: categories
-code_dir: downloads/code
-i18n_dir: :lang
-skip_render: README.md
-
-# Writing
-new_post_name: :year-:month-:day.:title.md # File name of new posts
-default_layout: post
-titlecase: false # Transform title into titlecase
-external_link: true # Open external links in new tab
-filename_case: 0
-render_drafts: false
-post_asset_folder: false
-relative_link: false
-future: true
-highlight:
- enable: true
- line_number: true
- auto_detect: false
- tab_replace:
-
-# Category & Tag
-default_category: uncategorized
-category_map:
-tag_map:
-
-# Date / Time format
-## Hexo uses Moment.js to parse and display date
-## You can customize the date format as defined in
-## http://momentjs.com/docs/#/displaying/format/
-date_format: YYYY-MM-DD
-time_format: HH:mm:ss
-
-# Pagination
-## Set per_page to 0 to disable pagination
-per_page: 10
-pagination_dir: page
-
-# Extensions
-## Plugins: https://hexo.io/plugins/
-## Themes: https://hexo.io/themes/
-theme: next
-
-# Deployment
-## Docs: https://hexo.io/docs/deployment.html
-deploy:
- type: git
- repo:
- github: https://github.com/xkcoding/xkcoding.github.io.git,master
- coding: https://git.dev.tencent.com/xkcoding/xkcoding.git,master
-
-#backup
-#backup:
-# type: git
-# repository:
-# github: https://github.com/xkcoding/MyBlog.git,master
-
-symbols_count_time:
- symbols: true
- time: true
- total_symbols: true
- total_time: true
-
-leancloud_counter_security:
- enable_sync: true
- app_id: igdqTfV9rbtm7WF3pmcBo0fM-gzGzoHsz
- app_key: qJ9XOC1VMiPdYow77o4YVWMN
- server_url: https://analyse.xkcoding.com
- username: xkcoding
- password: leancloud_counter!
-
-markdown:
- render:
- html: true # 在 markdown 文本中支持 html tag 标签
- xhtmlOut: false # 需要 xtml 文档,使用 替代
- breaks: true # 用 开始新的一行
- linkify: true # 自动将 可能是链接的内容转换成链接
- typographer: true # 印刷标识转换
- plugins:
- - markdown-it-abbr
- - markdown-it-footnote
- - markdown-it-ins
- - markdown-it-sub
- - markdown-it-sup
- - markdown-it-emoji
- - markdown-it-task-lists
- anchors:
- level: 2
- collisionSuffix: ''
- permalink: false,
- permalinkClass: 'header-anchor'
- permalinkSymbol: ''
- case: 0
- separator: ''
\ No newline at end of file
diff --git a/astro.config.ts b/astro.config.ts
new file mode 100644
index 00000000..5383dba2
--- /dev/null
+++ b/astro.config.ts
@@ -0,0 +1,84 @@
+import { defineConfig, envField, fontProviders } from "astro/config";
+import tailwindcss from "@tailwindcss/vite";
+import sitemap from "@astrojs/sitemap";
+import remarkToc from "remark-toc";
+import remarkCollapse from "remark-collapse";
+import {
+ transformerNotationDiff,
+ transformerNotationHighlight,
+ transformerNotationWordHighlight,
+} from "@shikijs/transformers";
+import { transformerFileName } from "./src/utils/transformers/fileName";
+import { transformerLangLabel } from "./src/utils/transformers/langLabel";
+import { SITE } from "./src/config";
+
+// https://astro.build/config
+export default defineConfig({
+ site: SITE.website,
+ trailingSlash: "never",
+ build: {
+ format: "file",
+ },
+ integrations: [
+ sitemap({
+ filter: page => SITE.showArchives || !page.endsWith("/archives"),
+ }),
+ ],
+ markdown: {
+ remarkPlugins: [remarkToc, [remarkCollapse, { test: "Table of contents" }]],
+ shikiConfig: {
+ // For more themes, visit https://shiki.style/themes
+ themes: { light: "min-light", dark: "night-owl" },
+ defaultColor: false,
+ wrap: false,
+ transformers: [
+ transformerFileName({ style: "v2", hideDot: false }),
+ transformerLangLabel(),
+ transformerNotationHighlight(),
+ transformerNotationWordHighlight(),
+ transformerNotationDiff({ matchAlgorithm: "v3" }),
+ ],
+ },
+ },
+ vite: {
+ // eslint-disable-next-line
+ // @ts-ignore
+ // This will be fixed in Astro 6 with Vite 7 support
+ // See: https://github.com/withastro/astro/issues/14030
+ plugins: [tailwindcss()],
+ optimizeDeps: {
+ exclude: ["@resvg/resvg-js"],
+ },
+ server: {
+ watch: {
+ ignored: ["**/openspec/**", "**/hexo-backup/**"],
+ },
+ },
+ },
+ image: {
+ responsiveStyles: true,
+ layout: "constrained",
+ },
+ env: {
+ schema: {
+ PUBLIC_GOOGLE_SITE_VERIFICATION: envField.string({
+ access: "public",
+ context: "client",
+ optional: true,
+ }),
+ },
+ },
+ experimental: {
+ preserveScriptOrder: true,
+ fonts: [
+ {
+ name: "Google Sans Code",
+ cssVariable: "--font-google-sans-code",
+ provider: fontProviders.google(),
+ fallbacks: ["monospace"],
+ weights: [300, 400, 500, 600, 700],
+ styles: ["normal", "italic"],
+ },
+ ],
+ },
+});
diff --git a/cz.yaml b/cz.yaml
new file mode 100644
index 00000000..cc639d65
--- /dev/null
+++ b/cz.yaml
@@ -0,0 +1,7 @@
+---
+commitizen:
+ name: cz_conventional_commits
+ tag_format: v$version
+ update_changelog_on_bump: true
+ version_provider: npm
+ version_scheme: semver
diff --git a/default.conf b/default.conf
deleted file mode 100644
index 18dd4d25..00000000
--- a/default.conf
+++ /dev/null
@@ -1,26 +0,0 @@
-server {
- listen 80;
- listen [::]:80;
- server_name localhost;
-
- gzip on;
- gzip_proxied any;
- gzip_min_length 1100;
- gzip_buffers 16 8k;
- gzip_types text/plain text/css application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript;
-
- location / {
- root /usr/share/nginx/html;
- index index.html index.htm;
- }
-
- error_page 404 /404.html;
- location = /404.html {
- root /usr/share/nginx/html;
- }
-
- error_page 500 502 503 504 /50x.html;
- location = /50x.html {
- root /usr/share/nginx/html;
- }
-}
\ No newline at end of file
diff --git a/eslint.config.js b/eslint.config.js
new file mode 100644
index 00000000..62b6f389
--- /dev/null
+++ b/eslint.config.js
@@ -0,0 +1,26 @@
+import eslintPluginAstro from "eslint-plugin-astro";
+import globals from "globals";
+import tseslint from "typescript-eslint";
+
+export default [
+ ...tseslint.configs.recommended,
+ ...eslintPluginAstro.configs.recommended,
+ {
+ languageOptions: {
+ globals: {
+ ...globals.browser,
+ ...globals.node,
+ },
+ },
+ },
+ { rules: { "no-console": "error" } },
+ {
+ ignores: [
+ "dist/**",
+ ".astro",
+ "public/pagefind/**",
+ "hexo-backup/**",
+ "openspec/**",
+ ],
+ },
+];
diff --git a/gulpfile.js b/gulpfile.js
deleted file mode 100644
index dacebf10..00000000
--- a/gulpfile.js
+++ /dev/null
@@ -1,33 +0,0 @@
-var gulp = require('gulp');
-var minifycss = require('gulp-minify-css');
-var uglify = require('gulp-uglify-es').default;
-var htmlmin = require('gulp-htmlmin');
-var htmlclean = require('gulp-htmlclean');
-// 压缩 public 目录 css
-gulp.task('minify-css', function () {
- return gulp.src('./public/**/*.css')
- .pipe(minifycss())
- .pipe(gulp.dest('./public'));
-});
-// 压缩 public 目录 html
-gulp.task('minify-html', function () {
- return gulp.src('./public/**/*.html')
- .pipe(htmlclean())
- .pipe(htmlmin({
- removeComments: true,
- minifyJS: true,
- minifyCSS: true,
- minifyURLs: true,
- }))
- .pipe(gulp.dest('./public'));
-});
-// 压缩 public/js 目录 js
-gulp.task('minify-js', function () {
- return gulp.src('./public/**/*.js')
- .pipe(uglify())
- .pipe(gulp.dest('./public'));
-});
-// 执行 gulp 命令时执行的任务
-gulp.task('default', [
- 'minify-html', 'minify-css', 'minify-js'
-]);
\ No newline at end of file
diff --git a/myblog.yml b/myblog.yml
deleted file mode 100644
index cb0af87f..00000000
--- a/myblog.yml
+++ /dev/null
@@ -1,40 +0,0 @@
-version: "3.8"
-
-services:
- __APP_NAME__:
- image: __IMAGE_NAME__
- networks:
- - traefik
- deploy:
- mode: replicated
- # 2个副本
- replicas: 2
- # 更新策略
- update_config:
- # 同时只能更新一个节点
- parallelism: 1
- delay: 10s
- order: stop-first
- placement:
- # 每个节点最多副本数量为 1
- max_replicas_per_node: 1
- constraints:
- - "node.labels.deploy==common"
- labels:
- - "traefik.enable=true"
- - "traefik.docker.network=traefik"
- - "traefik.http.routers.__APP_NAME__0.middlewares=https-redirect@file"
- - "traefik.http.routers.__APP_NAME__0.entrypoints=http"
- - "traefik.http.routers.__APP_NAME__0.rule=Host(`__APP_DOMAIN__`)"
- - "traefik.http.routers.__APP_NAME__1.middlewares=content-compress@file"
- - "traefik.http.routers.__APP_NAME__1.entrypoints=https"
- - "traefik.http.routers.__APP_NAME__1.tls=true"
- - "traefik.http.routers.__APP_NAME__1.tls.certresolver=le"
- #- "traefik.http.routers.__APP_NAME__1.tls.domains[0].main=__APP_DOMAIN__"
- #- "traefik.http.routers.__APP_NAME__1.tls.domains[0].sans=*.__APP_DOMAIN__"
- - "traefik.http.routers.__APP_NAME__1.rule=Host(`__APP_DOMAIN__`)"
- - "traefik.http.services.__APP_NAME__backend.loadbalancer.server.scheme=http"
- - "traefik.http.services.__APP_NAME__backend.loadbalancer.server.port=__CONTAINER_PORT__"
-networks:
- traefik:
- external: true
diff --git a/openspec/AGENTS.md b/openspec/AGENTS.md
new file mode 100644
index 00000000..6c1703ee
--- /dev/null
+++ b/openspec/AGENTS.md
@@ -0,0 +1,456 @@
+# OpenSpec Instructions
+
+Instructions for AI coding assistants using OpenSpec for spec-driven development.
+
+## TL;DR Quick Checklist
+
+- Search existing work: `openspec spec list --long`, `openspec list` (use `rg` only for full-text search)
+- Decide scope: new capability vs modify existing capability
+- Pick a unique `change-id`: kebab-case, verb-led (`add-`, `update-`, `remove-`, `refactor-`)
+- Scaffold: `proposal.md`, `tasks.md`, `design.md` (only if needed), and delta specs per affected capability
+- Write deltas: use `## ADDED|MODIFIED|REMOVED|RENAMED Requirements`; include at least one `#### Scenario:` per requirement
+- Validate: `openspec validate [change-id] --strict --no-interactive` and fix issues
+- Request approval: Do not start implementation until proposal is approved
+
+## Three-Stage Workflow
+
+### Stage 1: Creating Changes
+Create proposal when you need to:
+- Add features or functionality
+- Make breaking changes (API, schema)
+- Change architecture or patterns
+- Optimize performance (changes behavior)
+- Update security patterns
+
+Triggers (examples):
+- "Help me create a change proposal"
+- "Help me plan a change"
+- "Help me create a proposal"
+- "I want to create a spec proposal"
+- "I want to create a spec"
+
+Loose matching guidance:
+- Contains one of: `proposal`, `change`, `spec`
+- With one of: `create`, `plan`, `make`, `start`, `help`
+
+Skip proposal for:
+- Bug fixes (restore intended behavior)
+- Typos, formatting, comments
+- Dependency updates (non-breaking)
+- Configuration changes
+- Tests for existing behavior
+
+**Workflow**
+1. Review `openspec/project.md`, `openspec list`, and `openspec list --specs` to understand current context.
+2. Choose a unique verb-led `change-id` and scaffold `proposal.md`, `tasks.md`, optional `design.md`, and spec deltas under `openspec/changes//`.
+3. Draft spec deltas using `## ADDED|MODIFIED|REMOVED Requirements` with at least one `#### Scenario:` per requirement.
+4. Run `openspec validate --strict --no-interactive` and resolve any issues before sharing the proposal.
+
+### Stage 2: Implementing Changes
+Track these steps as TODOs and complete them one by one.
+1. **Read proposal.md** - Understand what's being built
+2. **Read design.md** (if exists) - Review technical decisions
+3. **Read tasks.md** - Get implementation checklist
+4. **Implement tasks sequentially** - Complete in order
+5. **Confirm completion** - Ensure every item in `tasks.md` is finished before updating statuses
+6. **Update checklist** - After all work is done, set every task to `- [x]` so the list reflects reality
+7. **Approval gate** - Do not start implementation until the proposal is reviewed and approved
+
+### Stage 3: Archiving Changes
+After deployment, create separate PR to:
+- Move `changes/[name]/` → `changes/archive/YYYY-MM-DD-[name]/`
+- Update `specs/` if capabilities changed
+- Use `openspec archive --skip-specs --yes` for tooling-only changes (always pass the change ID explicitly)
+- Run `openspec validate --strict --no-interactive` to confirm the archived change passes checks
+
+## Before Any Task
+
+**Context Checklist:**
+- [ ] Read relevant specs in `specs/[capability]/spec.md`
+- [ ] Check pending changes in `changes/` for conflicts
+- [ ] Read `openspec/project.md` for conventions
+- [ ] Run `openspec list` to see active changes
+- [ ] Run `openspec list --specs` to see existing capabilities
+
+**Before Creating Specs:**
+- Always check if capability already exists
+- Prefer modifying existing specs over creating duplicates
+- Use `openspec show [spec]` to review current state
+- If request is ambiguous, ask 1–2 clarifying questions before scaffolding
+
+### Search Guidance
+- Enumerate specs: `openspec spec list --long` (or `--json` for scripts)
+- Enumerate changes: `openspec list` (or `openspec change list --json` - deprecated but available)
+- Show details:
+ - Spec: `openspec show --type spec` (use `--json` for filters)
+ - Change: `openspec show --json --deltas-only`
+- Full-text search (use ripgrep): `rg -n "Requirement:|Scenario:" openspec/specs`
+
+## Quick Start
+
+### CLI Commands
+
+```bash
+# Essential commands
+openspec list # List active changes
+openspec list --specs # List specifications
+openspec show [item] # Display change or spec
+openspec validate [item] # Validate changes or specs
+openspec archive [--yes|-y] # Archive after deployment (add --yes for non-interactive runs)
+
+# Project management
+openspec init [path] # Initialize OpenSpec
+openspec update [path] # Update instruction files
+
+# Interactive mode
+openspec show # Prompts for selection
+openspec validate # Bulk validation mode
+
+# Debugging
+openspec show [change] --json --deltas-only
+openspec validate [change] --strict --no-interactive
+```
+
+### Command Flags
+
+- `--json` - Machine-readable output
+- `--type change|spec` - Disambiguate items
+- `--strict` - Comprehensive validation
+- `--no-interactive` - Disable prompts
+- `--skip-specs` - Archive without spec updates
+- `--yes`/`-y` - Skip confirmation prompts (non-interactive archive)
+
+## Directory Structure
+
+```
+openspec/
+├── project.md # Project conventions
+├── specs/ # Current truth - what IS built
+│ └── [capability]/ # Single focused capability
+│ ├── spec.md # Requirements and scenarios
+│ └── design.md # Technical patterns
+├── changes/ # Proposals - what SHOULD change
+│ ├── [change-name]/
+│ │ ├── proposal.md # Why, what, impact
+│ │ ├── tasks.md # Implementation checklist
+│ │ ├── design.md # Technical decisions (optional; see criteria)
+│ │ └── specs/ # Delta changes
+│ │ └── [capability]/
+│ │ └── spec.md # ADDED/MODIFIED/REMOVED
+│ └── archive/ # Completed changes
+```
+
+## Creating Change Proposals
+
+### Decision Tree
+
+```
+New request?
+├─ Bug fix restoring spec behavior? → Fix directly
+├─ Typo/format/comment? → Fix directly
+├─ New feature/capability? → Create proposal
+├─ Breaking change? → Create proposal
+├─ Architecture change? → Create proposal
+└─ Unclear? → Create proposal (safer)
+```
+
+### Proposal Structure
+
+1. **Create directory:** `changes/[change-id]/` (kebab-case, verb-led, unique)
+
+2. **Write proposal.md:**
+```markdown
+# Change: [Brief description of change]
+
+## Why
+[1-2 sentences on problem/opportunity]
+
+## What Changes
+- [Bullet list of changes]
+- [Mark breaking changes with **BREAKING**]
+
+## Impact
+- Affected specs: [list capabilities]
+- Affected code: [key files/systems]
+```
+
+3. **Create spec deltas:** `specs/[capability]/spec.md`
+```markdown
+## ADDED Requirements
+### Requirement: New Feature
+The system SHALL provide...
+
+#### Scenario: Success case
+- **WHEN** user performs action
+- **THEN** expected result
+
+## MODIFIED Requirements
+### Requirement: Existing Feature
+[Complete modified requirement]
+
+## REMOVED Requirements
+### Requirement: Old Feature
+**Reason**: [Why removing]
+**Migration**: [How to handle]
+```
+If multiple capabilities are affected, create multiple delta files under `changes/[change-id]/specs//spec.md`—one per capability.
+
+4. **Create tasks.md:**
+```markdown
+## 1. Implementation
+- [ ] 1.1 Create database schema
+- [ ] 1.2 Implement API endpoint
+- [ ] 1.3 Add frontend component
+- [ ] 1.4 Write tests
+```
+
+5. **Create design.md when needed:**
+Create `design.md` if any of the following apply; otherwise omit it:
+- Cross-cutting change (multiple services/modules) or a new architectural pattern
+- New external dependency or significant data model changes
+- Security, performance, or migration complexity
+- Ambiguity that benefits from technical decisions before coding
+
+Minimal `design.md` skeleton:
+```markdown
+## Context
+[Background, constraints, stakeholders]
+
+## Goals / Non-Goals
+- Goals: [...]
+- Non-Goals: [...]
+
+## Decisions
+- Decision: [What and why]
+- Alternatives considered: [Options + rationale]
+
+## Risks / Trade-offs
+- [Risk] → Mitigation
+
+## Migration Plan
+[Steps, rollback]
+
+## Open Questions
+- [...]
+```
+
+## Spec File Format
+
+### Critical: Scenario Formatting
+
+**CORRECT** (use #### headers):
+```markdown
+#### Scenario: User login success
+- **WHEN** valid credentials provided
+- **THEN** return JWT token
+```
+
+**WRONG** (don't use bullets or bold):
+```markdown
+- **Scenario: User login** ❌
+**Scenario**: User login ❌
+### Scenario: User login ❌
+```
+
+Every requirement MUST have at least one scenario.
+
+### Requirement Wording
+- Use SHALL/MUST for normative requirements (avoid should/may unless intentionally non-normative)
+
+### Delta Operations
+
+- `## ADDED Requirements` - New capabilities
+- `## MODIFIED Requirements` - Changed behavior
+- `## REMOVED Requirements` - Deprecated features
+- `## RENAMED Requirements` - Name changes
+
+Headers matched with `trim(header)` - whitespace ignored.
+
+#### When to use ADDED vs MODIFIED
+- ADDED: Introduces a new capability or sub-capability that can stand alone as a requirement. Prefer ADDED when the change is orthogonal (e.g., adding "Slash Command Configuration") rather than altering the semantics of an existing requirement.
+- MODIFIED: Changes the behavior, scope, or acceptance criteria of an existing requirement. Always paste the full, updated requirement content (header + all scenarios). The archiver will replace the entire requirement with what you provide here; partial deltas will drop previous details.
+- RENAMED: Use when only the name changes. If you also change behavior, use RENAMED (name) plus MODIFIED (content) referencing the new name.
+
+Common pitfall: Using MODIFIED to add a new concern without including the previous text. This causes loss of detail at archive time. If you aren’t explicitly changing the existing requirement, add a new requirement under ADDED instead.
+
+Authoring a MODIFIED requirement correctly:
+1) Locate the existing requirement in `openspec/specs//spec.md`.
+2) Copy the entire requirement block (from `### Requirement: ...` through its scenarios).
+3) Paste it under `## MODIFIED Requirements` and edit to reflect the new behavior.
+4) Ensure the header text matches exactly (whitespace-insensitive) and keep at least one `#### Scenario:`.
+
+Example for RENAMED:
+```markdown
+## RENAMED Requirements
+- FROM: `### Requirement: Login`
+- TO: `### Requirement: User Authentication`
+```
+
+## Troubleshooting
+
+### Common Errors
+
+**"Change must have at least one delta"**
+- Check `changes/[name]/specs/` exists with .md files
+- Verify files have operation prefixes (## ADDED Requirements)
+
+**"Requirement must have at least one scenario"**
+- Check scenarios use `#### Scenario:` format (4 hashtags)
+- Don't use bullet points or bold for scenario headers
+
+**Silent scenario parsing failures**
+- Exact format required: `#### Scenario: Name`
+- Debug with: `openspec show [change] --json --deltas-only`
+
+### Validation Tips
+
+```bash
+# Always use strict mode for comprehensive checks
+openspec validate [change] --strict --no-interactive
+
+# Debug delta parsing
+openspec show [change] --json | jq '.deltas'
+
+# Check specific requirement
+openspec show [spec] --json -r 1
+```
+
+## Happy Path Script
+
+```bash
+# 1) Explore current state
+openspec spec list --long
+openspec list
+# Optional full-text search:
+# rg -n "Requirement:|Scenario:" openspec/specs
+# rg -n "^#|Requirement:" openspec/changes
+
+# 2) Choose change id and scaffold
+CHANGE=add-two-factor-auth
+mkdir -p openspec/changes/$CHANGE/{specs/auth}
+printf "## Why\n...\n\n## What Changes\n- ...\n\n## Impact\n- ...\n" > openspec/changes/$CHANGE/proposal.md
+printf "## 1. Implementation\n- [ ] 1.1 ...\n" > openspec/changes/$CHANGE/tasks.md
+
+# 3) Add deltas (example)
+cat > openspec/changes/$CHANGE/specs/auth/spec.md << 'EOF'
+## ADDED Requirements
+### Requirement: Two-Factor Authentication
+Users MUST provide a second factor during login.
+
+#### Scenario: OTP required
+- **WHEN** valid credentials are provided
+- **THEN** an OTP challenge is required
+EOF
+
+# 4) Validate
+openspec validate $CHANGE --strict --no-interactive
+```
+
+## Multi-Capability Example
+
+```
+openspec/changes/add-2fa-notify/
+├── proposal.md
+├── tasks.md
+└── specs/
+ ├── auth/
+ │ └── spec.md # ADDED: Two-Factor Authentication
+ └── notifications/
+ └── spec.md # ADDED: OTP email notification
+```
+
+auth/spec.md
+```markdown
+## ADDED Requirements
+### Requirement: Two-Factor Authentication
+...
+```
+
+notifications/spec.md
+```markdown
+## ADDED Requirements
+### Requirement: OTP Email Notification
+...
+```
+
+## Best Practices
+
+### Simplicity First
+- Default to <100 lines of new code
+- Single-file implementations until proven insufficient
+- Avoid frameworks without clear justification
+- Choose boring, proven patterns
+
+### Complexity Triggers
+Only add complexity with:
+- Performance data showing current solution too slow
+- Concrete scale requirements (>1000 users, >100MB data)
+- Multiple proven use cases requiring abstraction
+
+### Clear References
+- Use `file.ts:42` format for code locations
+- Reference specs as `specs/auth/spec.md`
+- Link related changes and PRs
+
+### Capability Naming
+- Use verb-noun: `user-auth`, `payment-capture`
+- Single purpose per capability
+- 10-minute understandability rule
+- Split if description needs "AND"
+
+### Change ID Naming
+- Use kebab-case, short and descriptive: `add-two-factor-auth`
+- Prefer verb-led prefixes: `add-`, `update-`, `remove-`, `refactor-`
+- Ensure uniqueness; if taken, append `-2`, `-3`, etc.
+
+## Tool Selection Guide
+
+| Task | Tool | Why |
+|------|------|-----|
+| Find files by pattern | Glob | Fast pattern matching |
+| Search code content | Grep | Optimized regex search |
+| Read specific files | Read | Direct file access |
+| Explore unknown scope | Task | Multi-step investigation |
+
+## Error Recovery
+
+### Change Conflicts
+1. Run `openspec list` to see active changes
+2. Check for overlapping specs
+3. Coordinate with change owners
+4. Consider combining proposals
+
+### Validation Failures
+1. Run with `--strict` flag
+2. Check JSON output for details
+3. Verify spec file format
+4. Ensure scenarios properly formatted
+
+### Missing Context
+1. Read project.md first
+2. Check related specs
+3. Review recent archives
+4. Ask for clarification
+
+## Quick Reference
+
+### Stage Indicators
+- `changes/` - Proposed, not yet built
+- `specs/` - Built and deployed
+- `archive/` - Completed changes
+
+### File Purposes
+- `proposal.md` - Why and what
+- `tasks.md` - Implementation steps
+- `design.md` - Technical decisions
+- `spec.md` - Requirements and behavior
+
+### CLI Essentials
+```bash
+openspec list # What's in progress?
+openspec show [item] # View details
+openspec validate --strict --no-interactive # Is it correct?
+openspec archive [--yes|-y] # Mark complete (add --yes for automation)
+```
+
+Remember: Specs are truth. Changes are proposals. Keep them in sync.
diff --git a/openspec/changes/archive/refine-design-system/design.md b/openspec/changes/archive/refine-design-system/design.md
new file mode 100644
index 00000000..d8d7cead
--- /dev/null
+++ b/openspec/changes/archive/refine-design-system/design.md
@@ -0,0 +1,121 @@
+# Design: Refine Design System
+
+## Current State Analysis
+
+### 现有配色方案
+
+```css
+/* Light Mode */
+--background: #fdfdfd; /* 近白色背景 */
+--foreground: #282728; /* 深灰前景 */
+--accent: #006cac; /* 蓝色强调 */
+--muted: #e6e6e6; /* 浅灰静音色 */
+--border: #ece9e9; /* 淡边框 */
+
+/* Dark Mode */
+--background: #212737; /* 深蓝灰背景 */
+--foreground: #eaedf3; /* 浅色前景 */
+--accent: #ff6b01; /* 橙色强调 */
+--muted: #343f60; /* 深蓝静音色 */
+--border: #ab4b08; /* 橙棕边框 - 过于强烈 */
+```
+
+### 现有 Logo 分析
+
+- **概念**:代码日记本 (日记本 + `>` 代码符号)
+- **结构**:矩形日记本 + 装订线 + 装订孔 + 代码符号 + 底部横线
+- **颜色**:使用 CSS 变量 `--accent` 动态适配主题
+
+## Design Decisions
+
+### DD-1: 配色优化策略
+
+**决策**:在现有配色基础上进行微调,保持风格一致性
+
+**理由**:
+- 用户明确表示保持现有风格
+- 现有配色已建立视觉识别
+- 仅需解决对比度和协调性问题
+
+**具体调整**:
+
+| 变量 | Light Mode | Dark Mode | 调整说明 |
+|------|------------|-----------|----------|
+| `--background` | **重新设计** | 保持 | 亮色背景需优化 |
+| `--foreground` | **重新设计** | 保持 | 配合新背景 |
+| `--accent` | **重新设计** | 保持/微调 | 亮色主题色需重新选择 |
+| `--muted` | **重新设计** | 保持 | 配合整体配色 |
+| `--border` | **重新设计** | 保持/微调 | 配合整体配色 |
+
+**设计方向**:
+- 亮色模式:完全重新设计,使用**紫色系**作为强调色(用户偏好)
+- 暗色模式:基本保持现有配色(#212737 背景 + #ff6b01 橙色强调色的组合效果良好)
+
+**亮色模式配色思路**:
+- `--accent`:紫色系(如 #7c3aed、#8b5cf6、#6d28d9 等)
+- 整体配色围绕紫色强调色构建和谐的色彩体系
+
+### DD-2: Logo 重设计方向
+
+**决策**:保持"代码日记本"核心概念,优化视觉细节
+
+**设计原则**:
+1. **简洁性**:减少不必要的装饰元素
+2. **可识别性**:在小尺寸下保持清晰
+3. **一致性**:与整体设计语言协调
+4. **适应性**:支持亮色/暗色模式
+
+**优化方向**:
+- 优化日记本形状比例
+- 简化装订线设计
+- 增强代码符号的视觉重量
+- 移除底部横线(降低复杂度)
+
+### DD-3: 色彩对比度标准
+
+**决策**:遵循 WCAG 2.1 AA 标准
+
+| 使用场景 | 最低对比度 |
+|---------|-----------|
+| 正文文本 | 4.5:1 |
+| 大号文本 (≥18pt) | 3:1 |
+| UI 组件边界 | 3:1 |
+
+## Technical Approach
+
+### 文件修改范围
+
+```
+src/
+├── styles/
+│ └── global.css # 配色变量
+└── assets/
+ └── icons/
+ └── IconLogo.svg # Logo 重设计
+```
+
+### CSS 变量结构(保持不变)
+
+```css
+@theme inline {
+ --color-background: var(--background);
+ --color-foreground: var(--foreground);
+ --color-accent: var(--accent);
+ --color-muted: var(--muted);
+ --color-border: var(--border);
+}
+```
+
+## Alternatives Considered
+
+### Alt-1: 完全重新设计配色
+
+**否决原因**:用户明确要求保持现有风格
+
+### Alt-2: 使用设计 Token 系统
+
+**否决原因**:当前规模不需要额外复杂度,现有 CSS 变量方案已足够
+
+### Alt-3: Logo 使用渐变效果
+
+**否决原因**:增加复杂度,不利于小尺寸显示和打印
diff --git a/openspec/changes/archive/refine-design-system/proposal.md b/openspec/changes/archive/refine-design-system/proposal.md
new file mode 100644
index 00000000..a446ef1c
--- /dev/null
+++ b/openspec/changes/archive/refine-design-system/proposal.md
@@ -0,0 +1,59 @@
+# Proposal: Refine Design System
+
+## Summary
+
+优化博客的设计系统,在保持现有风格基础上微调配色方案,提升亮色/暗色模式的视觉体验,并重新设计 Logo 图标以增强品牌辨识度。
+
+## Motivation
+
+当前设计系统存在以下可优化点:
+
+1. **亮色模式配色不理想**:当前亮色模式配色不够美观,需要重新设计
+2. **暗色模式体验良好**:保持现有暗色配色,仅做微调
+3. **Logo 设计**:当前 Logo 较为基础,品牌辨识度可进一步提升
+
+## Scope
+
+### In Scope
+
+- 优化亮色/暗色模式的 5 个核心 CSS 变量
+- 重新设计 `IconLogo.svg`,提升品牌辨识度
+- 确保 WCAG 2.1 AA 级对比度标准
+
+### Out of Scope
+
+- 不更换 Tabler Icons 图标库
+- 不新增图标类型或动画效果
+- 不改变整体设计风格
+
+## Approach
+
+使用 UI/UX Pro Max skill 进行专业的设计优化:
+
+1. **配色优化**:基于色彩理论微调现有配色,提升和谐度
+2. **对比度校验**:使用 WCAG 标准验证所有颜色组合
+3. **Logo 重设计**:保持"代码日记本"概念,提升视觉精致度
+
+## Success Criteria
+
+- [ ] 所有文本/背景组合达到 WCAG AA 对比度 (≥4.5:1)
+- [ ] 亮色/暗色模式切换视觉体验流畅
+- [ ] Logo 在 16px-128px 尺寸范围内清晰可辨
+- [ ] 主观评估:配色更加协调美观
+
+## Risks & Mitigations
+
+| Risk | Impact | Mitigation |
+|------|--------|------------|
+| 配色变化影响现有内容可读性 | Medium | 在多种内容类型上测试 |
+| Logo 变化影响品牌认知 | Low | 保持核心概念,仅优化细节 |
+
+## Dependencies
+
+- UI/UX Pro Max skill 用于专业设计指导
+
+## Timeline Estimate
+
+- 设计分析与方案制定:1 个工作单元
+- 配色实现与测试:1 个工作单元
+- Logo 重设计:1 个工作单元
diff --git a/openspec/changes/archive/refine-design-system/specs/color-palette/spec.md b/openspec/changes/archive/refine-design-system/specs/color-palette/spec.md
new file mode 100644
index 00000000..d05fb80b
--- /dev/null
+++ b/openspec/changes/archive/refine-design-system/specs/color-palette/spec.md
@@ -0,0 +1,89 @@
+# Spec: Color Palette
+
+## Overview
+
+定义博客设计系统的配色规范,确保视觉一致性和可访问性。
+
+## MODIFIED Requirements
+
+### Requirement: Light Mode Color Palette
+
+亮色模式配色方案需要重新设计,以提供更好的视觉体验。
+
+#### Scenario: 亮色模式默认配色
+
+**Given** 用户访问博客且系统处于亮色模式
+**When** 页面加载完成
+**Then** 应用以下 CSS 变量配色:
+- `--background`: 需优化(当前 #fdfdfd)
+- `--foreground`: 需优化(当前 #282728)
+- `--accent`: **紫色系**(当前 #006cac 蓝色 → 改为紫色)
+- `--muted`: 需优化(当前 #e6e6e6)
+- `--border`: 需优化(当前 #ece9e9)
+
+**Note**:
+- 强调色使用紫色系(用户偏好)
+- 具体颜色值将在 UI/UX Pro Max 分析后确定,确保与紫色强调色协调
+
+#### Scenario: 亮色模式文本对比度
+
+**Given** 亮色模式配色方案
+**When** 计算 foreground 与 background 的对比度
+**Then** 对比度应 ≥4.5:1 (WCAG AA)
+
+#### Scenario: 亮色模式强调色对比度
+
+**Given** 亮色模式配色方案
+**When** accent 色用于链接或按钮文本
+**Then** accent 与 background 对比度应 ≥4.5:1
+
+---
+
+### Requirement: Dark Mode Color Palette
+
+暗色模式配色方案基本符合预期,仅需微调优化。
+
+#### Scenario: 暗色模式默认配色
+
+**Given** 用户访问博客且系统处于暗色模式
+**When** 页面加载完成
+**Then** 应用以下 CSS 变量配色:
+- `--background`: #212737(保持)
+- `--foreground`: #eaedf3(保持)
+- `--accent`: #ff6b01(保持或微调)
+- `--muted`: #343f60(保持)
+- `--border`: 需评估是否微调(当前 #ab4b08)
+
+#### Scenario: 暗色模式文本对比度
+
+**Given** 暗色模式配色方案
+**When** 计算 foreground 与 background 的对比度
+**Then** 对比度应 ≥4.5:1 (WCAG AA)
+
+---
+
+### Requirement: Theme Consistency
+
+亮色和暗色模式应保持设计语言一致性。
+
+#### Scenario: 主题切换视觉连贯
+
+**Given** 用户在亮色模式下浏览页面
+**When** 切换到暗色模式
+**Then** 视觉风格应保持连贯,仅明暗反转
+
+#### Scenario: 强调色语义一致
+
+**Given** 两种主题模式
+**When** accent 色用于相同类型的 UI 元素
+**Then** 用户应能识别其为强调/交互元素
+
+---
+
+## Acceptance Criteria
+
+- [ ] 亮色模式配色重新设计,符合用户审美偏好
+- [ ] 暗色模式配色维持或微调
+- [ ] 所有颜色组合符合 WCAG 2.1 AA 标准
+- [ ] CSS 变量在 `global.css` 中正确定义
+- [ ] 主题切换平滑无闪烁
diff --git a/openspec/changes/archive/refine-design-system/specs/logo-design/spec.md b/openspec/changes/archive/refine-design-system/specs/logo-design/spec.md
new file mode 100644
index 00000000..aad8e614
--- /dev/null
+++ b/openspec/changes/archive/refine-design-system/specs/logo-design/spec.md
@@ -0,0 +1,97 @@
+# Spec: Logo Design
+
+## Overview
+
+重新设计博客 Logo,提升品牌辨识度和视觉精致度。
+
+## MODIFIED Requirements
+
+### Requirement: Logo Visual Design
+
+Logo 需在保持"代码日记本"核心概念的基础上优化视觉表现。
+
+#### Scenario: Logo 核心概念
+
+**Given** Logo 设计需求
+**When** 设计 Logo 图形
+**Then** 应保留以下核心元素:
+- 日记本/笔记本形状
+- 代码符号 `>`
+- 简洁现代的视觉风格
+
+#### Scenario: Logo 视觉简化
+
+**Given** 当前 Logo 包含多个装饰元素
+**When** 重新设计 Logo
+**Then** 应简化以下元素:
+- 装订线设计更简洁
+- 减少或移除底部横线
+- 优化代码符号的视觉比重
+
+#### Scenario: Logo 比例优化
+
+**Given** Logo 需要在不同尺寸下使用
+**When** 设计 Logo 比例
+**Then** 应确保:
+- 主要元素在 16px 尺寸下可辨识
+- 视觉重心平衡
+- 留白适当
+
+---
+
+### Requirement: Logo Technical Implementation
+
+Logo 的技术实现需支持动态主题适配。
+
+#### Scenario: SVG 格式要求
+
+**Given** Logo 文件格式为 SVG
+**When** 实现 Logo
+**Then** 应满足:
+- viewBox 设置为 "0 0 128 128"
+- 使用 CSS 变量 `var(--accent)` 作为主色
+- stroke/fill 属性支持 currentColor
+
+#### Scenario: 主题适配
+
+**Given** Logo 使用 CSS 变量定义颜色
+**When** 用户切换主题
+**Then** Logo 颜色应自动适配当前主题的 accent 色
+
+#### Scenario: Logo 小尺寸清晰度
+
+**Given** Logo 用于 favicon (16px)
+**When** 缩小到 16x16 像素
+**Then** 核心图形(日记本 + 代码符号)应清晰可辨
+
+---
+
+### Requirement: Logo File Structure
+
+保持现有的 Logo 文件组织结构。
+
+#### Scenario: 文件位置
+
+**Given** Logo SVG 文件
+**When** 保存文件
+**Then** 应位于 `src/assets/icons/IconLogo.svg`
+
+#### Scenario: 样式定义
+
+**Given** Logo SVG 内部样式
+**When** 定义颜色类
+**Then** 应使用内联 `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/public/robots.txt b/public/robots.txt
new file mode 100644
index 00000000..d01461ba
--- /dev/null
+++ b/public/robots.txt
@@ -0,0 +1,4 @@
+User-agent: *
+Allow: /
+
+Sitemap: https://xkcoding.com/sitemap-index.xml
diff --git a/resources/_gen/assets/css/style.scss_1a67ae4ed98f18e3ea7da02d2ccd80c9.content b/resources/_gen/assets/css/style.scss_1a67ae4ed98f18e3ea7da02d2ccd80c9.content
new file mode 100644
index 00000000..e51a13b7
--- /dev/null
+++ b/resources/_gen/assets/css/style.scss_1a67ae4ed98f18e3ea7da02d2ccd80c9.content
@@ -0,0 +1,3 @@
+details.center summary{text-align:center}:root{--fi-global-font-color: #161209;--fi-global-border-color: #f0f0f0;--fi-primary: #1772ee;--fi-secondary: #919191;--fi-success: #13ce66;--fi-info: #0dcaf0;--fi-warning: #ffba00;--fi-danger: #ff4949;--fi-scroll-mt: calc(3.5rem + .5rem);--fi-breadcrumb-height: 0px;--fi-hr-background-color: #e3e3e3;--fi-hr-before-color: #ec638c;--fi-divider-edge-weak: linear-gradient(to right, transparent, var(--fi-divider-bg, #d0d0d5), transparent);--fi-scrollbar-thumb-color: #a6a6a6;--fi-scrollbar-thumb-hover-color: #7d7d7d;--fi-scrollbar-track-color: transparent;--fi-scrollbar-width: thin;--fi-scrollbar-width-legacy: 12px}[data-theme=dark]{--fi-global-font-color: #b1b1ba;--fi-global-border-color: #363636;--fi-primary: #1066db;--fi-secondary: #848484;--fi-success: #11b75a;--fi-info: #0cb6d8;--fi-warning: #e6a700;--fi-danger: #ff3030;--fi-hr-background-color: #434343;--fi-hr-before-color: #f5f5f5}/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:0.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace, monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace, monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-0.25em}sup{top:-0.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}button,[type="button"],[type="reset"],[type="submit"]{-webkit-appearance:button}button::-moz-focus-inner,[type="button"]::-moz-focus-inner,[type="reset"]::-moz-focus-inner,[type="submit"]::-moz-focus-inner{border-style:none;padding:0}button:-moz-focusring,[type="button"]:-moz-focusring,[type="reset"]:-moz-focusring,[type="submit"]:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:0.35em 0.75em 0.625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type="checkbox"],[type="radio"]{box-sizing:border-box;padding:0}[type="number"]::-webkit-inner-spin-button,[type="number"]::-webkit-outer-spin-button{height:auto}[type="search"]{-webkit-appearance:textfield;outline-offset:-2px}[type="search"]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}html{font-family:system-ui,-apple-system,BlinkMacSystemFont,PingFang SC,Microsoft YaHei UI,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,Helvetica,Arial,sans-serif;font-weight:400;font-display:swap;font-size:16px;line-height:1.5rem;width:100%;scroll-behavior:smooth;overflow:overlay}@media screen and (prefers-reduced-motion: reduce){html{scroll-behavior:auto}}::selection{background-color:rgba(53,166,247,0.25)}[data-theme='dark'] ::selection{background-color:rgba(50,112,194,0.4)}body{background-color:#fff;color:#161209;word-wrap:break-word;overflow-wrap:break-word}[data-theme='dark'] body{color:#b1b1ba;background-color:#292a2e}iframe{border:0;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}[role='button']{cursor:pointer}summary{cursor:pointer}abbr[title]{cursor:help;text-decoration-skip-ink:none}img{object-fit:cover}img,video{max-width:100%;height:auto}a,a::before,a::after{text-decoration:none;color:#161209}[data-theme='dark'] a,[data-theme='dark'] a::before,[data-theme='dark'] a::after{color:#b1b1ba}a:active,a:hover{color:#2983bb}[data-theme='dark'] a:active,[data-theme='dark'] a:hover{color:#fff}@supports (scrollbar-width: auto){*{scrollbar-color:var(--fi-scrollbar-thumb-color) var(--fi-scrollbar-track-color);scrollbar-width:var(--fi-scrollbar-width)}}::-webkit-scrollbar{height:var(--fi-scrollbar-width-legacy);width:var(--fi-scrollbar-width-legacy);overflow:visible}::-webkit-scrollbar-button{height:0;width:0}::-webkit-scrollbar-corner{background-color:var(--fi-scrollbar-track-color)}::-webkit-scrollbar-thumb{background-color:var(--fi-scrollbar-thumb-color)}::-webkit-scrollbar-thumb:hover{background-color:var(--fi-scrollbar-thumb-hover-color)}::-webkit-scrollbar-track{background-color:var(--fi-scrollbar-track-color)}::-webkit-scrollbar-thumb,::-webkit-scrollbar-track{background-clip:padding-box;border:3px solid transparent;border-radius:100px}#mask{background-repeat:no-repeat;background-position:center;position:fixed;top:0;left:0;width:100%;height:100%;background-color:rgba(0,0,0,0);z-index:-1}.blur #mask{z-index:100;background-color:rgba(0,0,0,0.25)}img{-webkit-border-radius:2.5px;-moz-border-radius:2.5px;border-radius:2.5px}svg.icon{display:inline-block;width:1.25em;height:1em;text-align:center}svg.icon path{fill:currentColor}img.emoji{display:inline-block;height:1em;width:1em;margin:0 0.05em 0 0.1em;vertical-align:-0.1em}.hugo-icon,.fixit-icon{display:inline-block;height:1.2em;width:1.2em;line-height:1.2em;position:relative;vertical-align:text-bottom}.details>.details-summary{-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.details>.details-summary:hover{cursor:pointer}.details>.details-summary i.details-icon{color:#b1b1ba;-webkit-transition:transform 0.2s ease;-moz-transition:transform 0.2s ease;-o-transition:transform 0.2s ease;transition:transform 0.2s ease}[data-theme='dark'] .details>.details-summary i.details-icon{color:#909092}.details>.details-content{max-height:0;overflow-y:hidden;-webkit-transition:max-height 0.2s cubic-bezier(0, 1, 0, 1) -0.1s;-moz-transition:max-height 0.2s cubic-bezier(0, 1, 0, 1) -0.1s;-o-transition:max-height 0.2s cubic-bezier(0, 1, 0, 1) -0.1s;transition:max-height 0.2s cubic-bezier(0, 1, 0, 1) -0.1s}.details.open>.details-summary{-webkit-border-radius:5px 5px 0 0;-moz-border-radius:5px 5px 0 0;border-radius:5px 5px 0 0}.details.open>.details-summary i.details-icon{-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.details.open>.details-content{max-height:100%;-webkit-border-radius:0 0 5px 5px;-moz-border-radius:0 0 5px 5px;border-radius:0 0 5px 5px;-webkit-transition:max-height 0.2s cubic-bezier(0.5, 0, 1, 0) 0s;-moz-transition:max-height 0.2s cubic-bezier(0.5, 0, 1, 0) 0s;-o-transition:max-height 0.2s cubic-bezier(0.5, 0, 1, 0) 0s;transition:max-height 0.2s cubic-bezier(0.5, 0, 1, 0) 0s}.details.disabled>.details-summary{cursor:default}.cc-window.cc-banner .cc-btn{color:#161209}.cc-window.cc-banner .cc-btn:hover,.cc-window.cc-banner .cc-btn:focus{background-color:#ccc}[data-theme='dark'] .cc-window.cc-banner .cc-btn{color:#161209}[data-theme='dark'] .cc-window.cc-banner .cc-btn:hover,[data-theme='dark'] .cc-window.cc-banner .cc-btn:focus{background-color:#fff}.fixed-button{position:fixed;right:1.5rem;font-size:1rem;line-height:1.5rem;padding:0.5rem 0.625rem;color:#b1b1ba;background-color:#f8f8f8;z-index:100;-webkit-border-radius:1.25rem;-moz-border-radius:1.25rem;border-radius:1.25rem;-webkit-transition:color 0.4s ease;-moz-transition:color 0.4s ease;-o-transition:color 0.4s ease;transition:color 0.4s ease}.blur .fixed-button{-webkit-filter:blur(1.5px);filter:blur(1.5px)}.fixed-button:hover,.fixed-button:active{color:#161209;cursor:pointer}.fixed-button:active,.fixed-button:focus,.fixed-button:hover{outline:none}[data-theme='dark'] .fixed-button{color:#909092;background-color:#252627}[data-theme='dark'] .fixed-button:hover,[data-theme='dark'] .fixed-button:active{color:#b1b1ba}.back-to-top{bottom:1.5rem}.view-comments{bottom:4.5rem}@keyframes octocat-wave{0%,100%{-webkit-transform:rotate(0);-ms-transform:rotate(0);-o-transform:rotate(0);transform:rotate(0)}20%,60%{-webkit-transform:rotate(-25deg);-ms-transform:rotate(-25deg);-o-transform:rotate(-25deg);transform:rotate(-25deg)}40%,80%{-webkit-transform:rotate(10deg);-ms-transform:rotate(10deg);-o-transform:rotate(10deg);transform:rotate(10deg)}}.github-corner{position:fixed;top:0;z-index:200}.github-corner :hover .octo-arm{animation:octocat-wave 560ms ease-in-out}.github-corner svg{width:3.5rem;height:3.5rem;border:0;color:#fff;fill:#252627}[data-theme='dark'] .github-corner svg{color:#000;fill:#f8f8f8}.github-corner.left{left:0}.github-corner.left svg{-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);-o-transform:scale(-1, 1);transform:scale(-1, 1)}.github-corner.right{right:0}.noscript-warning{background-color:var(--fi-danger);color:white;font-size:1rem;font-weight:bold;position:fixed;text-align:center;left:0;bottom:0;width:100%;z-index:200}[data-theme='dark'] .noscript-warning{background-color:var(--fi-danger-dark)}.reading-progress-bar{--progress: 0;height:var(--progress-h, 2px);width:var(--progress);background-color:var(--bg-progress, var(--fi-info));position:fixed;z-index:200}[data-theme="dark"] .reading-progress-bar{background-color:var(--bg-progress-dark, var(--fi-info-dark))}.typeit{--ti-cursor-font-family: system-ui,-apple-system,BlinkMacSystemFont,PingFang SC,Microsoft YaHei UI,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,Helvetica,Arial,sans-serif;--ti-cursor-transform: translateX(0)}.typeit .highlight{padding:0.375rem;font-size:0.875rem;font-family:Source Code Pro,Menlo,Consolas,Monaco,monospace,system-ui,-apple-system,BlinkMacSystemFont,PingFang SC,Microsoft YaHei UI,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,Helvetica,Arial,sans-serif;font-weight:bold;word-break:break-all;white-space:pre-wrap}.wrapper{display:flex;flex-direction:column;min-height:100vh;width:100%}.wrapper main{flex:1 0 auto}.wrapper main aside{flex:1;padding:0.5rem}.wrapper main aside:not(:has(~aside)){display:none}.wrapper main.container{display:flex;align-items:flex-start;justify-content:center;padding-inline:1rem;gap:0.5rem}.wrapper main.container.container-reverse{flex-direction:row-reverse}.wrapper main.container:has(.home:not(.posts)){align-items:center}header{width:100%;background-color:#f8f8f8;z-index:200;-webkit-transition:box-shadow 0.3s ease;-moz-transition:box-shadow 0.3s ease;-o-transition:box-shadow 0.3s ease;transition:box-shadow 0.3s ease}[data-theme='dark'] header{background-color:#252627}header:hover{-webkit-box-shadow:0 0 1.5rem 0 rgba(0,0,0,0.1);box-shadow:0 0 1.5rem 0 rgba(0,0,0,0.1)}[data-theme='dark'] header:hover{-webkit-box-shadow:0 0 1.5rem 0 rgba(255,255,255,0.1);box-shadow:0 0 1.5rem 0 rgba(255,255,255,0.1)}.header-wrapper{display:flex;justify-content:space-between;align-items:center;box-sizing:border-box;width:100%}.header-title{font-family:system-ui,-apple-system,BlinkMacSystemFont,PingFang SC,Microsoft YaHei UI,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,Helvetica,Arial,sans-serif;font-weight:bold;margin-right:0.5rem;min-width:10%;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;-webkit-flex:10;flex:10}.header-title .logo{min-height:1.5em;height:1.5em;vertical-align:middle;width:auto}.header-title .header-title-text{margin-left:0.25rem;margin-right:0.25rem}.header-title .header-subtitle{margin-left:0.25rem;font-size:0.6em}.menu{margin:0;padding:0}.menu .menu-item-divider{list-style:none;margin:0.25rem 0.5rem;height:1px;background-color:#e3e3e3}[data-theme='dark'] .menu .menu-item-divider{background-color:#434343}.menu .menu-item{list-style:none}.menu .menu-item .dropdown-icon{margin-left:0.25rem;font-size:.875em;color:#161209}[data-theme='dark'] .menu .menu-item .dropdown-icon{color:#b1b1ba}.menu .menu-item .dropdown-icon.open::before{content:"\f078"}.menu .menu-item.active>.menu-link,.menu .menu-item.active>.nested-item>.menu-link{font-weight:bold;color:#161209}[data-theme='dark'] .menu .menu-item.active>.menu-link,[data-theme='dark'] .menu .menu-item.active>.nested-item>.menu-link{color:#fff}.menu .menu-item .menu-link[href="javascript:void(0);"]{color:inherit}.menu .menu-item .menu-link i{-webkit-transform:perspective(1rem);-ms-transform:perspective(1rem);-o-transform:perspective(1rem);transform:perspective(1rem);-webkit-transition:transform 0.4s ease;-moz-transition:transform 0.4s ease;-o-transition:transform 0.4s ease;transition:transform 0.4s ease}.menu .menu-item .menu-link:hover i{-webkit-transform:perspective(1rem) translateZ(0.2rem);-ms-transform:perspective(1rem) translateZ(0.2rem);-o-transform:perspective(1rem) translateZ(0.2rem);transform:perspective(1rem) translateZ(0.2rem)}.menu .menu-item.language-switch,.menu .menu-item.theme-switch{cursor:pointer}.menu .search{position:relative}.menu .search input{color:transparent;box-sizing:border-box;height:2.5rem;width:1.25rem;-webkit-border-radius:.5rem;-moz-border-radius:.5rem;border-radius:.5rem;border:none;outline:none;background-color:transparent;vertical-align:baseline !important;-webkit-transition:width 0.3s ease;-moz-transition:width 0.3s ease;-o-transition:width 0.3s ease;transition:width 0.3s ease}.menu .search input::-webkit-input-placeholder{color:rgba(0,0,0,0)}.menu .search input:-moz-placeholder{color:rgba(0,0,0,0)}.menu .search input::-moz-placeholder{color:rgba(0,0,0,0)}.menu .search input:-ms-input-placeholder{color:rgba(0,0,0,0)}.menu .search input::placeholder{color:rgba(0,0,0,0)}.menu .search .search-button{margin:0;position:absolute;left:auto;right:1rem}.menu .search .search-toggle{left:0.5rem;right:auto}#header-desktop.open .menu .search .search-toggle{left:1rem}.menu .search .search-loading{display:none}.menu .search .search-clear{display:none}.open .menu .search input,.menu .search.mobile input{color:#161209;background-color:#e9e9e9;padding:0 2rem 0 2rem}[data-theme='dark'] .open .menu .search input,[data-theme='dark'] .menu .search.mobile input{color:#b1b1ba;background-color:#363636}[data-theme='dark'] .open .menu .search input::-webkit-input-placeholder,[data-theme='dark'] .menu .search.mobile input::-webkit-input-placeholder{color:#909092}[data-theme='dark'] .open .menu .search input:-moz-placeholder,[data-theme='dark'] .menu .search.mobile input:-moz-placeholder{color:#909092}[data-theme='dark'] .open .menu .search input::-moz-placeholder,[data-theme='dark'] .menu .search.mobile input::-moz-placeholder{color:#909092}[data-theme='dark'] .open .menu .search input:-ms-input-placeholder,[data-theme='dark'] .menu .search.mobile input:-ms-input-placeholder{color:#909092}[data-theme='dark'] .open .menu .search input::placeholder,[data-theme='dark'] .menu .search.mobile input::placeholder{color:#909092}.open .menu .search input::-webkit-input-placeholder,.menu .search.mobile input::-webkit-input-placeholder{color:#b1b1ba}.open .menu .search input:-moz-placeholder,.menu .search.mobile input:-moz-placeholder{color:#b1b1ba}.open .menu .search input::-moz-placeholder,.menu .search.mobile input::-moz-placeholder{color:#b1b1ba}.open .menu .search input:-ms-input-placeholder,.menu .search.mobile input:-ms-input-placeholder{color:#b1b1ba}.open .menu .search input::placeholder,.menu .search.mobile input::placeholder{color:#b1b1ba}.open .menu .search .search-button,.menu .search.mobile .search-button{color:#b1b1ba}[data-theme='dark'] .open .menu .search .search-button,[data-theme='dark'] .menu .search.mobile .search-button{color:#909092}.open .menu .search .search-clear:hover,.menu .search.mobile .search-clear:hover{color:#ff6b6b}.open .menu .search .search-toggle:hover,.menu .search.mobile .search-toggle:hover{cursor:default}.theme-switch i{-webkit-transform:rotate(225deg);-ms-transform:rotate(225deg);-o-transform:rotate(225deg);transform:rotate(225deg);-webkit-transition:transform 0.4s ease;-moz-transition:transform 0.4s ease;-o-transition:transform 0.4s ease;transition:transform 0.4s ease}.theme-switch:hover i{-webkit-transform:rotate(45deg);-ms-transform:rotate(45deg);-o-transform:rotate(45deg);transform:rotate(45deg)}#header-desktop{display:block;position:sticky;top:0;height:3.5rem;line-height:3.5rem;box-sizing:border-box}[data-header-desktop='normal'] #header-desktop{position:static}#header-desktop nav{overflow:hidden;white-space:nowrap}#header-desktop .header-wrapper{padding:0 2rem 0 MAX(10vh, 3.5rem);height:100%}#header-desktop .header-wrapper[data-github-corner='right']{padding-right:3.5rem}#header-desktop .header-wrapper .header-title{font-size:1.5rem;height:100%}#header-desktop .header-wrapper .menu{display:flex;flex-direction:row;flex-wrap:nowrap;float:right}#header-desktop .header-wrapper .menu>.menu-item:hover .sub-menu{display:block;-webkit-transform:translateX(-0.5rem);-ms-transform:translateX(-0.5rem);-o-transform:translateX(-0.5rem);transform:translateX(-0.5rem)}#header-desktop .header-wrapper .menu>.menu-item.language-switch:hover .sub-menu{-webkit-transform:translateX(calc(1.75rem - 100%));-ms-transform:translateX(calc(1.75rem - 100%));-o-transform:translateX(calc(1.75rem - 100%));transform:translateX(calc(1.75rem - 100%))}#header-desktop .header-wrapper .menu .menu-item{padding:0 0.5rem}#header-desktop .header-wrapper .menu .menu-item.delimiter{width:0;margin:0.125rem 0.25rem;padding:0;border-left:1px dashed #dadade}[data-theme='dark'] #header-desktop .header-wrapper .menu .menu-item.delimiter{border-left-color:#6a6a6c}#header-desktop .header-wrapper .menu .sub-menu{position:absolute;margin:0;padding:0.25rem 0;display:none;top:calc(3.5rem - 0.75rem);border-radius:5px;border:1px solid #e3e3e3;background-color:#f8f8f8;max-height:60vh;overflow-y:auto;-webkit-transition:box-shadow 0.3s ease;-moz-transition:box-shadow 0.3s ease;-o-transition:box-shadow 0.3s ease;transition:box-shadow 0.3s ease;-webkit-box-shadow:0 0 1.5rem 0 rgba(0,0,0,0.1);box-shadow:0 0 1.5rem 0 rgba(0,0,0,0.1)}[data-theme='dark'] #header-desktop .header-wrapper .menu .sub-menu{background-color:#252627;border-color:#434343;-webkit-box-shadow:0 0 1.5rem 0 rgba(255,255,255,0.1);box-shadow:0 0 1.5rem 0 rgba(255,255,255,0.1)}#header-desktop .header-wrapper .menu .sub-menu .menu-item{height:1.75rem;line-height:1.75rem;max-width:15rem;white-space:nowrap;text-overflow:ellipsis;overflow-x:hidden}#header-desktop.open .header-wrapper .menu .menu-item.search input{width:24rem}#header-mobile{display:none;position:sticky;top:0;min-height:3.5rem;line-height:3.5rem}[data-header-mobile='normal'] #header-mobile{position:static}#header-mobile .header-container{padding:0;margin:0}#header-mobile .header-container .header-wrapper{padding:0 1rem;font-size:1.125rem;-webkit-transition:margin-top 0.3s ease;-moz-transition:margin-top 0.3s ease;-o-transition:margin-top 0.3s ease;transition:margin-top 0.3s ease}#header-mobile .header-container .header-wrapper .header-title{font-size:1.5rem;max-width:80%}#header-mobile .header-container .header-wrapper .menu-toggle{line-height:4rem;cursor:pointer;-webkit-transition:width 0.3s ease;-moz-transition:width 0.3s ease;-o-transition:width 0.3s ease;transition:width 0.3s ease}#header-mobile .header-container .header-wrapper .menu-toggle span{display:block;background:#161209;width:1.5rem;height:2px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-transition:all 0.3s ease-in-out;-moz-transition:all 0.3s ease-in-out;-o-transition:all 0.3s ease-in-out;transition:all 0.3s ease-in-out}[data-theme='dark'] #header-mobile .header-container .header-wrapper .menu-toggle span{background:#b1b1ba}#header-mobile .header-container .header-wrapper .menu-toggle span:nth-child(1){margin-bottom:0.5rem}#header-mobile .header-container .header-wrapper .menu-toggle span:nth-child(3){margin-top:0.5rem}#header-mobile .header-container .header-wrapper .menu-toggle.active span:nth-child(1){-webkit-transform:rotate(45deg) translate(0.4rem, 0.5rem);-ms-transform:rotate(45deg) translate(0.4rem, 0.5rem);-o-transform:rotate(45deg) translate(0.4rem, 0.5rem);transform:rotate(45deg) translate(0.4rem, 0.5rem)}#header-mobile .header-container .header-wrapper .menu-toggle.active span:nth-child(2){opacity:0}#header-mobile .header-container .header-wrapper .menu-toggle.active span:nth-child(3){-webkit-transform:rotate(-45deg) translate(0.4rem, -0.5rem);-ms-transform:rotate(-45deg) translate(0.4rem, -0.5rem);-o-transform:rotate(-45deg) translate(0.4rem, -0.5rem);transform:rotate(-45deg) translate(0.4rem, -0.5rem)}#header-mobile .header-container .menu{background:#f8f8f8;border-top:2px solid #f0f0f0;display:none;overflow-y:overlay;max-height:70vh;-webkit-box-shadow:0 0.125rem 0.25rem rgba(0,0,0,0.1);box-shadow:0 0.125rem 0.25rem rgba(0,0,0,0.1);--fi-scrollbar-width: none;--fi-scrollbar-width-legacy: 0}[data-theme='dark'] #header-mobile .header-container .menu{background:#252627;border-top-color:#363636;-webkit-box-shadow:0 0.125rem 0.25rem rgba(255,255,255,0.1);box-shadow:0 0.125rem 0.25rem rgba(255,255,255,0.1)}#header-mobile .header-container .menu .search-wrapper{display:flex;justify-content:space-between;align-items:center;box-sizing:border-box;padding:.5rem 1rem;line-height:2.5rem;position:sticky;top:0;background-color:#f8f8f8;z-index:50}[data-theme='dark'] #header-mobile .header-container .menu .search-wrapper{background-color:#252627}#header-mobile .header-container .menu .search{flex-grow:10}#header-mobile .header-container .menu .search .algolia-autocomplete,#header-mobile .header-container .menu .search input{width:100%}#header-mobile .header-container .menu .search-loading,#header-mobile .header-container .menu .search-clear{right:0.5rem}#header-mobile .header-container .menu .search-button{top:0}#header-mobile .header-container .menu .search-cancel{display:none;margin-left:0.75rem}#header-mobile .header-container .menu .menu-item{display:block;position:relative;line-height:2.5rem;padding:0 1rem}#header-mobile .header-container .menu .menu-item:not(.menu-system):hover{background-color:#f2f2f3}[data-theme='dark'] #header-mobile .header-container .menu .menu-item:not(.menu-system):hover{background-color:#535355}#header-mobile .header-container .menu .menu-item .menu-link{display:block;white-space:nowrap;text-overflow:ellipsis;overflow-x:hidden;-webkit-flex:4;flex:4}#header-mobile .header-container .menu .menu-item .nested-item{display:flex;justify-content:space-between;align-items:center}#header-mobile .header-container .menu .menu-item .nested-item .menu-link{max-width:80%}#header-mobile .header-container .menu .menu-system{display:flex;justify-content:center;gap:0.25rem;position:sticky;bottom:0;line-height:2.5rem;padding:0 1rem;background-color:#f8f8f8}[data-theme='dark'] #header-mobile .header-container .menu .menu-system{background-color:#252627}#header-mobile .header-container .menu .menu-system .menu-system-item{padding-inline:0.5rem}#header-mobile .header-container .menu .menu-system .language-switch{position:relative}#header-mobile .header-container .menu .menu-system .language-switch .language-select{position:absolute;opacity:0;left:0;top:0;width:100%;height:100%}#header-mobile .header-container .menu .sub-menu{padding-left:0;display:none}#header-mobile .header-container .menu .sub-menu.open{display:block}#header-mobile .header-container .menu .sub-menu .menu-item:hover{background-color:#e7e7ea}[data-theme='dark'] #header-mobile .header-container .menu .sub-menu .menu-item:hover{background-color:#5d5d5f}#header-mobile .header-container .menu.active{display:block}#header-mobile.open .header-wrapper{margin-top:-3.5rem}#header-mobile.open .menu{padding-top:0;border-top:none}#header-mobile.open .menu .menu-item{display:none}#header-mobile.open .menu .search-cancel{display:inline}.search-dropdown{position:fixed;top:3.5rem;z-index:200;-webkit-box-shadow:0 0.125rem 0.25rem rgba(0,0,0,0.1);box-shadow:0 0.125rem 0.25rem rgba(0,0,0,0.1)}[data-theme='dark'] .search-dropdown{-webkit-box-shadow:0 0.125rem 0.25rem rgba(255,255,255,0.1);box-shadow:0 0.125rem 0.25rem rgba(255,255,255,0.1)}.search-dropdown.desktop{right:2rem;width:30rem}.search-dropdown.mobile{right:0;width:100%}.search-dropdown .dropdown-menu{right:0 !important;background-color:#fff}[data-theme='dark'] .search-dropdown .dropdown-menu{background-color:#292a2e}.search-dropdown .dropdown-menu .suggestions{overflow-y:auto;max-height:calc(100vh - 3.5rem)}.search-dropdown .dropdown-menu .suggestions .suggestion{padding:0.75rem 1rem}.search-dropdown .dropdown-menu .suggestions .suggestion .suggestion-title{display:inline-block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:1rem;font-weight:bold;max-width:75%}.search-dropdown .dropdown-menu .suggestions .suggestion .suggestion-date{font-size:0.875rem;float:right;text-align:right;color:#b1b1ba}[data-theme='dark'] .search-dropdown .dropdown-menu .suggestions .suggestion .suggestion-date{color:#909092}.search-dropdown .dropdown-menu .suggestions .suggestion .suggestion-context{line-height:1.25rem;display:-moz-box;display:-webkit-box;display:box;-webkit-box-orient:vertical;-moz-box-orient:vertical;box-orient:vertical;-webkit-line-clamp:2;overflow:hidden;text-overflow:ellipsis;word-wrap:break-word;overflow-wrap:break-word;color:#b1b1ba}[data-theme='dark'] .search-dropdown .dropdown-menu .suggestions .suggestion .suggestion-context{color:#909092}.search-dropdown .dropdown-menu .suggestions .suggestion em{font-style:normal;background-color:rgba(53,166,247,0.25)}[data-theme='dark'] .search-dropdown .dropdown-menu .suggestions .suggestion em{background-color:rgba(50,112,194,0.4)}.search-dropdown .dropdown-menu .suggestions .suggestion.cursor{background:#e6ebf1}[data-theme='dark'] .search-dropdown .dropdown-menu .suggestions .suggestion.cursor{background:#384049}.search-dropdown .dropdown-menu .suggestions .suggestion:hover{cursor:pointer}.search-dropdown .dropdown-menu .search-empty{padding:1rem;text-align:center}.search-dropdown .dropdown-menu .search-empty .search-query{font-weight:bold}[data-theme='dark'] .search-dropdown .dropdown-menu .search-empty .search-query{color:#ddd}.search-dropdown .dropdown-menu .search-footer{padding:0.5rem 1rem;float:right;font-size:0.8rem;color:#b1b1ba}.search-dropdown .dropdown-menu .search-footer [data-theme='dark']{color:#909092}.search-dropdown .dropdown-menu .search-footer a,.search-dropdown .dropdown-menu .search-footer a::before,.search-dropdown .dropdown-menu .search-footer a::after{text-decoration:none;color:#2376b7}[data-theme='dark'] .search-dropdown .dropdown-menu .search-footer a,[data-theme='dark'] .search-dropdown .dropdown-menu .search-footer a::before,[data-theme='dark'] .search-dropdown .dropdown-menu .search-footer a::after{color:#1781b5}.search-dropdown .dropdown-menu .search-footer a:active,.search-dropdown .dropdown-menu .search-footer a:hover{color:#ea517f}[data-theme='dark'] .search-dropdown .dropdown-menu .search-footer a:active,[data-theme='dark'] .search-dropdown .dropdown-menu .search-footer a:hover{color:#cc5595}.search-dropdown .dropdown-menu .search-footer a{font-size:1rem}.breadcrumb-container{padding-inline:1rem;font-size:0.75rem;background-color:#fff;z-index:100}[data-theme='dark'] .breadcrumb-container{background-color:#292a2e}.breadcrumb-container.sticky{position:sticky;top:3.5rem}.breadcrumb-container .breadcrumb{display:flex;flex-wrap:wrap;padding:0.5rem 0;margin:0 auto;list-style:none;border-bottom:1px inset #f0f0f0}[data-theme='dark'] .breadcrumb-container .breadcrumb{border-bottom-color:#363636}.breadcrumb-container .breadcrumb .breadcrumb-item{display:flex}.breadcrumb-container .breadcrumb .breadcrumb-item+.breadcrumb-item::before{content:attr(data-separator);display:inline-block;padding-inline:0.5rem;color:#b1b1ba}[data-theme='dark'] .breadcrumb-container .breadcrumb .breadcrumb-item+.breadcrumb-item::before{color:#909092}.breadcrumb-container .breadcrumb .breadcrumb-item.active{color:var(--fi-secondary)}footer{height:auto;width:100%;text-align:center;line-height:1.25rem;padding:1rem 0}footer .footer-container{display:flex;flex-direction:column;font-size:0.875rem}footer .footer-container .footer-line{width:100%}footer .footer-container .footer-line .footer-divider:not(:first-child)::before{content:'|';margin-left:0.25rem;margin-right:0.25rem}footer .footer-container .footer-line .animate-icon{color:#dc143c;animation:icon-animate 1.33s ease-in-out infinite}footer .footer-container .footer-line .run-times{font-variant-numeric:tabular-nums lining-nums}.blur footer{-webkit-filter:blur(1.5px);filter:blur(1.5px)}@keyframes icon-animate{0%,100%{-webkit-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);transform:scale(1)}10%,30%{-webkit-transform:scale(0.9);-ms-transform:scale(0.9);-o-transform:scale(0.9);transform:scale(0.9)}20%,40%,60%,80%{-webkit-transform:scale(1.1);-ms-transform:scale(1.1);-o-transform:scale(1.1);transform:scale(1.1)}50%,70%{-webkit-transform:scale(1.1);-ms-transform:scale(1.1);-o-transform:scale(1.1);transform:scale(1.1)}}.pagination{display:flex;flex-direction:row;justify-content:center;flex-wrap:wrap;list-style:none;white-space:nowrap;width:100%;padding:1rem 0}.pagination a{font-size:0.8rem;color:#bfbfbf;letter-spacing:0.1rem;font-weight:700;padding:5px 5px;text-decoration:none;-webkit-transition:0.3s;-moz-transition:0.3s;-o-transition:0.3s;transition:0.3s}[data-theme='dark'] .pagination a{color:#b1b1ba}.pagination li{padding:5px;margin:0 10px;box-sizing:border-box;position:relative;display:inline}.pagination li.disabled{display:none}.pagination li:hover a{color:#000}[data-theme='dark'] .pagination li:hover a{color:#fff}.pagination li::before,.pagination li::after{position:absolute;content:'';width:0;height:3px;background:#000;-webkit-transition:0.3s;-moz-transition:0.3s;-o-transition:0.3s;transition:0.3s;bottom:0px}[data-theme='dark'] .pagination li::before,[data-theme='dark'] .pagination li::after{background:#fff}.pagination li::before{left:50%}.pagination li::after{right:50%}.pagination li:hover::before,.pagination li:hover::after{width:50%}.pagination li.active a{color:#000}[data-theme='dark'] .pagination li.active a{color:#fff}.pagination li.active::before,.pagination li.active::after{width:50%}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text-start{text-align:left !important}.text-end{text-align:right !important}.text-center{text-align:center !important}.d-none{display:none !important}.ms-1,.mx-1{margin-left:0.25em !important}.me-1,.mx-1{margin-right:0.25em !important}.ps-1,.px-1{padding-left:0.25em !important}.pe-1,.px-1{padding-right:0.25em !important}.text-primary{color:var(--fi-primary) !important}.text-secondary{color:var(--fi-secondary) !important}.text-success{color:var(--fi-success) !important}.text-info{color:var(--fi-info) !important}.text-warning{color:var(--fi-warning) !important}.text-danger{color:var(--fi-danger) !important}.order-first{order:-1 !important}.order-0{order:0 !important}.order-1{order:1 !important}.order-2{order:2 !important}.order-3{order:3 !important}.order-4{order:4 !important}.order-5{order:5 !important}.order-last{order:6 !important}.variant-numeric{font-variant-numeric:tabular-nums lining-nums}.page{position:relative}.page .content [id]{scroll-margin-top:var(--fi-scroll-mt)}.blur .page{-webkit-filter:blur(1.5px);filter:blur(1.5px)}.toc .toc-title{font-size:1rem;font-weight:bold;text-transform:uppercase}.toc .toc-content{font-size:.875rem}.toc .toc-content ul{text-indent:-0.8em;padding-left:0.8em;list-style:none}.toc .toc-content ul a:first-child::before{content:'|';font-weight:bolder;margin-right:0.5em;position:relative;bottom:2px;color:#2376b7}[data-theme='dark'] .toc .toc-content ul a:first-child::before{color:#1781b5}.toc .toc-content ul ul{padding-left:1em}.toc .toc-content ol{margin:0;padding-left:2.25em}.toc .toc-content ol ol{padding-left:1.25em}.toc .toc-content ol li:has(>a:hover)::marker{color:#2376b7}[data-theme='dark'] .toc .toc-content ol li:has(>a:hover)::marker{color:#1781b5}.toc ruby{background:#f6f8fa}.toc ruby rt{color:#b1b1ba}[data-theme='dark'] .toc ruby{background:#2d333b}[data-theme='dark'] .toc ruby rt{color:#909092}#toc-auto{display:block;position:sticky;top:calc(3.5rem + var(--fi-breadcrumb-height));box-sizing:border-box;visibility:hidden;word-wrap:break-word;overflow-wrap:break-word}.blur #toc-auto{-webkit-filter:blur(1.5px);filter:blur(1.5px)}[data-header-desktop='normal'] #toc-auto{--fi-scroll-mt: .5rem}#toc-auto .toc-title{margin-block:0 0.5em;cursor:pointer;-webkit-user-select:none !important;-moz-user-select:none !important;user-select:none !important}#toc-auto .toc-title::before{content:'|';font-weight:bolder;margin-right:0.5em;position:relative;bottom:2px;color:#f0f0f0}[data-theme='dark'] #toc-auto .toc-title::before{color:#363636}#toc-auto .toc-title i.toc-icon{-webkit-transition:transform 0.2s ease;-moz-transition:transform 0.2s ease;-o-transition:transform 0.2s ease;transition:transform 0.2s ease}#toc-auto.toc-hidden i.toc-icon{-webkit-transform:rotate(-90deg);-ms-transform:rotate(-90deg);-o-transform:rotate(-90deg);transform:rotate(-90deg);-webkit-transition:transform 0.2s ease;-moz-transition:transform 0.2s ease;-o-transition:transform 0.2s ease;transition:transform 0.2s ease}#toc-auto .toc-content{overflow-y:scroll;max-height:calc(100vh - 7rem);--fi-scrollbar-width: none;--fi-scrollbar-width-legacy: 0}#toc-auto .toc-content.always-active ul,#toc-auto .toc-content.always-active ol{display:block}#toc-auto .toc-content>nav>ul{margin:0}#toc-auto .toc-content li.has-active::marker{color:#2376b7}[data-theme='dark'] #toc-auto .toc-content li.has-active::marker{color:#1781b5}#toc-auto .toc-content li.has-active:has(>.active)::marker{font-weight:bold}#toc-auto .toc-content li.has-active>a{color:#2376b7}[data-theme='dark'] #toc-auto .toc-content li.has-active>a{color:#1781b5}#toc-auto .toc-content ul ul{display:none}#toc-auto .toc-content ul .has-active>ul{display:block}#toc-auto .toc-content ul a.active::before{color:#ea517f}[data-theme='dark'] #toc-auto .toc-content ul a.active::before{color:#cc5595}#toc-auto .toc-content ol ol{display:none}#toc-auto .toc-content ol .has-active>ol{display:block}#toc-auto .toc-content a.active{font-weight:bold}#toc-static{display:none;margin:0.8em 0}#toc-static[data-kept='true']{display:block}#toc-static .toc-title{display:flex;justify-content:space-between;line-height:2em;padding:0 0.75em;background:#ecf0f5}[data-theme='dark'] #toc-static .toc-title{background:#20242a}#toc-static .toc-content{background-color:#f6f8fa}#toc-static .toc-content>nav>ul{margin:0;padding:0.4em 1em 0.4em 1.8em}#toc-static .toc-content>nav>ul a:hover{font-weight:bold}#toc-static .toc-content>nav>ul a:hover::before{color:#ea517f}[data-theme='dark'] #toc-static .toc-content>nav>ul a:hover::before{color:#cc5595}#toc-static .toc-content>nav>ol{margin:0;padding:0.4em 1em 0.4em 2.25em}#toc-static .toc-content>nav>ol li:has(>a:hover)::marker,#toc-static .toc-content>nav>ol a:hover{font-weight:bold}[data-theme='dark'] #toc-static .toc-content{background-color:#2d333b}#toc-static.open .toc-title{background:#e2e9ef}[data-theme='dark'] #toc-static.open .toc-title{background:#262b32}.fixit-decryptor-container{font-family:system-ui,-apple-system,BlinkMacSystemFont,PingFang SC,Microsoft YaHei UI,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,Helvetica,Arial,sans-serif;text-align:center;margin-block:var(--fixit-decryptor-margin-block)}.fixit-decryptor-container .fixit-decryptor-loading{vertical-align:middle}.fixit-decryptor-container .fixit-decryptor-input,.fixit-decryptor-container .fixit-decryptor-btn,.fixit-decryptor-container .fixit-encryptor-btn{display:none;box-sizing:border-box;outline:none;color:#161209;border:1px solid #cacaca;border-radius:5px;-webkit-transition:all 0.1s ease-out;-moz-transition:all 0.1s ease-out;-o-transition:all 0.1s ease-out;transition:all 0.1s ease-out;box-shadow:0px 0px 5px 0px #cacaca}.fixit-decryptor-container .fixit-decryptor-input:active,.fixit-decryptor-container .fixit-decryptor-input:focus,.fixit-decryptor-container .fixit-decryptor-input:hover,.fixit-decryptor-container .fixit-decryptor-btn:active,.fixit-decryptor-container .fixit-decryptor-btn:focus,.fixit-decryptor-container .fixit-decryptor-btn:hover,.fixit-decryptor-container .fixit-encryptor-btn:active,.fixit-decryptor-container .fixit-encryptor-btn:focus,.fixit-decryptor-container .fixit-encryptor-btn:hover{color:#2371a2;border-color:#2983bb;box-shadow:0px 0px 5px 0px #6bb2df}[data-theme='dark'] .fixit-decryptor-container .fixit-decryptor-input:active,[data-theme='dark'] .fixit-decryptor-container .fixit-decryptor-input:focus,[data-theme='dark'] .fixit-decryptor-container .fixit-decryptor-input:hover,[data-theme='dark'] .fixit-decryptor-container .fixit-decryptor-btn:active,[data-theme='dark'] .fixit-decryptor-container .fixit-decryptor-btn:focus,[data-theme='dark'] .fixit-decryptor-container .fixit-decryptor-btn:hover,[data-theme='dark'] .fixit-decryptor-container .fixit-encryptor-btn:active,[data-theme='dark'] .fixit-decryptor-container .fixit-encryptor-btn:focus,[data-theme='dark'] .fixit-decryptor-container .fixit-encryptor-btn:hover{color:#fff;border-color:#fff;box-shadow:0px 0px 5px 0px #ccc}[data-theme='dark'] .fixit-decryptor-container .fixit-decryptor-input,[data-theme='dark'] .fixit-decryptor-container .fixit-decryptor-btn,[data-theme='dark'] .fixit-decryptor-container .fixit-encryptor-btn{color:#b1b1ba;border-color:#5c5c5c;box-shadow:0px 0px 5px 0px #5c5c5c}.fixit-decryptor-container .fixit-decryptor-input{width:calc(clamp(50%, 450px, 100%) - 100px);height:3rem;padding-left:1rem;padding-right:1rem;background-color:#fff}[data-theme='dark'] .fixit-decryptor-container .fixit-decryptor-input{background-color:#292a2e}.fixit-decryptor-container .fixit-decryptor-btn,.fixit-decryptor-container .fixit-encryptor-btn{cursor:pointer;-webkit-transition:all 0.1s ease-out;-moz-transition:all 0.1s ease-out;-o-transition:all 0.1s ease-out;transition:all 0.1s ease-out;padding:0.8rem 1rem;background-color:#f8f8f8}[data-theme='dark'] .fixit-decryptor-container .fixit-decryptor-btn,[data-theme='dark'] .fixit-decryptor-container .fixit-encryptor-btn{background-color:#252627}.fixit-decryptor-container .fixit-encryptor-btn{display:none}fixit-encryptor.initialized>.fixit-decryptor-container .fixit-decryptor-input,fixit-encryptor.initialized>.fixit-decryptor-container .fixit-decryptor-btn{display:inline-block}fixit-encryptor.initialized>.fixit-decryptor-container .fixit-decryptor-loading{display:none}fixit-encryptor cipher-text{display:none !important}article fixit-encryptor .fixit-decryptor-container{--fixit-decryptor-margin-block: 2rem}article fixit-encryptor.decrypted>.fixit-decryptor-container .fixit-decryptor-loading,article fixit-encryptor.decrypted>.fixit-decryptor-container .fixit-decryptor-input,article fixit-encryptor.decrypted>.fixit-decryptor-container .fixit-decryptor-btn{display:none}article fixit-encryptor.decrypted>.fixit-decryptor-container .fixit-encryptor-btn{display:inline-block}#content fixit-encryptor .fixit-decryptor-container{--fixit-decryptor-margin-block: 1rem}#content fixit-encryptor.decrypted>.fixit-decryptor-container{display:none}.encrypted-hidden{display:none !important}.collection-card{font-size:0.875rem;background-color:#f7f7f7;margin-block:1rem;padding:1rem 1.25rem;position:relative;-webkit-border-radius:7.5px;-moz-border-radius:7.5px;border-radius:7.5px;-webkit-user-select:none !important;-moz-user-select:none !important;user-select:none !important}[data-theme='dark'] .collection-card{background-color:#303136}.collection-card:has(.collection-nav-item:nth-child(2))::before{content:'';position:absolute;left:50%;bottom:0.75rem;width:1px;height:2rem;background:linear-gradient(to bottom, rgba(0,0,0,0.03), rgba(0,0,0,0.05) 50%, rgba(0,0,0,0.03) 100%)}[data-theme='dark'] .collection-card:has(.collection-nav-item:nth-child(2))::before{background:linear-gradient(to bottom, rgba(255,255,255,0.03), rgba(255,255,255,0.05) 50%, rgba(255,255,255,0.03) 100%)}.collection-card .collection-title a,.collection-card .collection-title a::before,.collection-card .collection-title a::after{text-decoration:none;color:#2376b7}[data-theme='dark'] .collection-card .collection-title a,[data-theme='dark'] .collection-card .collection-title a::before,[data-theme='dark'] .collection-card .collection-title a::after{color:#1781b5}.collection-card .collection-title a:active,.collection-card .collection-title a:hover{color:#ea517f}[data-theme='dark'] .collection-card .collection-title a:active,[data-theme='dark'] .collection-card .collection-title a:hover{color:#cc5595}.collection-card .collection-nav{display:flex;justify-content:space-between;gap:1rem}.collection-card .collection-nav:not(:empty){margin-top:0.5rem}.collection-card .collection-nav .collection-nav-item{flex-grow:1;max-width:calc(50% - 0.5rem);font-weight:500;display:flex;align-items:center;-webkit-transition:all 0.3s ease-out;-moz-transition:all 0.3s ease-out;-o-transition:all 0.3s ease-out;transition:all 0.3s ease-out}.collection-card .collection-nav .collection-nav-item:only-child{max-width:100%}.collection-card .collection-nav .collection-nav-item i{flex-shrink:0}.collection-card .collection-nav .collection-nav-item span{flex-grow:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.collection-card .collection-nav .collection-nav-item[rel='next']{text-align:right}.collection-card .collection-nav .collection-nav-item[rel='prev']:hover{-webkit-transform:translateX(-4px);-ms-transform:translateX(-4px);-o-transform:translateX(-4px);transform:translateX(-4px)}.collection-card .collection-nav .collection-nav-item[rel='next']:hover{-webkit-transform:translateX(4px);-ms-transform:translateX(4px);-o-transform:translateX(4px);transform:translateX(4px)}.aside-collection{position:sticky;top:calc(3.5rem + var(--fi-breadcrumb-height));box-sizing:border-box;word-wrap:break-word;overflow-wrap:break-word}.blur .aside-collection{-webkit-filter:blur(1.5px);filter:blur(1.5px)}.collection-details:not(:last-of-type){margin-bottom:0.5rem}.collection-details.open .collection-summary{background-color:#f5f5f5}[data-theme='dark'] .collection-details.open .collection-summary{background-color:#333439}.collection-details .collection-summary{display:flex;align-items:baseline;gap:0.25em;padding:0.2em 0.5em;background-color:#f7f7f7;-webkit-user-select:none !important;-moz-user-select:none !important;user-select:none !important}[data-theme='dark'] .collection-details .collection-summary{background-color:#303136}.collection-details .collection-summary>i.fa-layer-group{flex-shrink:0}.collection-details .collection-summary .collection-name{flex-grow:1;font-weight:bold;font-size:1rem}.collection-details .collection-summary .collection-name::before{content:attr(title) "・"}.collection-details .collection-summary .collection-count{flex-shrink:0;color:var(--fi-secondary)}.collection-details .collection-summary .details-icon{flex-shrink:0}.collection-details .collection-content{font-size:.875rem;background-color:#fafafa}[data-theme='dark'] .collection-details .collection-content{background-color:#2e2f33}.collection-details .collection-content .collection-list{margin:0;padding-inline:1.6em 0.8em;padding-block:0.4em;text-indent:-0.8em;list-style:none;overflow-y:scroll;max-height:60vh;--fi-scrollbar-width: none;--fi-scrollbar-width-legacy: 0}.collection-details .collection-content .collection-list .collection-item>a::before,.collection-details .collection-content .collection-list .collection-item>span::before{content:'|';font-weight:600;margin-right:0.5em;position:relative;bottom:2px;color:#2376b7}[data-theme='dark'] .collection-details .collection-content .collection-list .collection-item>a::before,[data-theme='dark'] .collection-details .collection-content .collection-list .collection-item>span::before{color:#1781b5}.collection-details .collection-content .collection-list .collection-item>.active{font-weight:bold;color:#2376b7}[data-theme='dark'] .collection-details .collection-content .collection-list .collection-item>.active{color:#1781b5}.collection-details .collection-content .collection-list .collection-item>.active::before{color:#ea517f}[data-theme='dark'] .collection-details .collection-content .collection-list .collection-item>.active::before{color:#cc5595}.collection-details .collection-content .collection-nav-simple{display:flex;align-items:center;justify-content:center;gap:0.5em;background-color:#f7f7f7}[data-theme='dark'] .collection-details .collection-content .collection-nav-simple{background-color:#303136}.collection-details .collection-content .collection-nav-simple a.collection-nav-item{padding-inline:2px;border-radius:50%;-webkit-transition:background-color 0.3s ease-out;-moz-transition:background-color 0.3s ease-out;-o-transition:background-color 0.3s ease-out;transition:background-color 0.3s ease-out}.collection-details .collection-content .collection-nav-simple a.collection-nav-item:hover{background-color:#e6e6e6}[data-theme='dark'] .collection-details .collection-content .collection-nav-simple a.collection-nav-item:hover{background-color:#414349}.collection-details .collection-content .collection-nav-simple>i.collection-nav-item{padding:4px 2px;cursor:not-allowed}.related-details:not(:first-of-type)::before{content:'';display:block;border:0;padding-top:1px;background:var(--fi-divider-edge-weak);margin-block:1rem 0.5rem}.related-details:not(:last-of-type){margin-bottom:0.5rem}.related-details .related-summary{display:flex;align-items:baseline;gap:0.25em;padding:0.2em 0.5em;-webkit-user-select:none !important;-moz-user-select:none !important;user-select:none !important}.related-details .related-summary>i.fa-layer-group{flex-shrink:0}.related-details .related-summary .related-title{font-weight:bold;font-size:1rem}.related-details .related-summary .related-count{flex-shrink:0;color:var(--fi-secondary)}.related-details .related-summary .details-icon{flex-shrink:0}.related-details .related-content{font-size:.875rem}.related-details .related-content .related-list{margin:0;padding-inline:1.6em 0.8em;padding-block:0.4em;text-indent:-0.8em;list-style:none;overflow-y:scroll;max-height:60vh;--fi-scrollbar-width: none;--fi-scrollbar-width-legacy: 0}.related-details .related-content .related-list .related-item>a::before,.related-details .related-content .related-list .related-item>span::before{content:'|';font-weight:600;margin-right:0.5em;position:relative;bottom:2px;color:#2376b7}[data-theme='dark'] .related-details .related-content .related-list .related-item>a::before,[data-theme='dark'] .related-details .related-content .related-list .related-item>span::before{color:#1781b5}.single .single-title{padding:1rem 0 0.5rem;margin:0;font-size:1.6rem;font-weight:bold;line-height:140%}.single .single-title .icon-pin{display:inline-block;width:max-content;transform:rotate(30deg);margin-right:0.25em;color:var(--fi-danger)}.single .single-title .icon-repost{display:inline-block;width:max-content;margin-right:0.25em;color:var(--fi-success)}.single .single-subtitle{margin-block:0 0.5rem;font-size:1.2rem;font-weight:normal;line-height:1.15}.single .post-meta{font-size:0.875rem;color:#b1b1ba}.single .post-meta span{display:inline-block}[data-theme='dark'] .single .post-meta{color:#909092}.single .post-meta a,.single .post-meta a::before,.single .post-meta a::after{text-decoration:none;color:#2376b7}[data-theme='dark'] .single .post-meta a,[data-theme='dark'] .single .post-meta a::before,[data-theme='dark'] .single .post-meta a::after{color:#b1b1ba}.single .post-meta a:active,.single .post-meta a:hover{color:#ea517f}[data-theme='dark'] .single .post-meta a:active,[data-theme='dark'] .single .post-meta a:hover{color:#fff}.single .post-meta .author{font-size:1.05rem}.single .post-meta .author img.avatar{width:1.3rem;height:1.3rem;border-radius:50%;vertical-align:text-bottom;border:0.25px solid #f0f0f0;box-sizing:border-box;object-position:center;-o-object-fit:cover;object-fit:cover;font-family:"object-fit: cover;"}[data-theme='dark'] .single .post-meta .author img.avatar{border-color:#363636}.single .featured-image{margin:1rem 0}.single .featured-image img{display:block;width:100%;max-width:100%;height:auto;margin:0 auto;overflow:hidden}.single #content[data-end-flag]:not([data-password])::after{content:attr(data-end-flag);display:block;text-align:center;color:#b1b1ba;font-size:15px;padding:5px}.single hr{position:relative;height:1em;margin-block:1em;background:linear-gradient(to right, var(--fi-hr-background-color) 50%, transparent 50%);background-size:10px 2px;background-position:center;background-repeat:repeat-x;border:none}.single hr.awesome-hr::before{display:inline-block;font-weight:600;font-family:'Font Awesome 6 Free';text-rendering:auto;-webkit-font-smoothing:antialiased;position:absolute;left:5%;top:50%;transform:translateY(-50%);z-index:1;color:var(--fi-hr-before-color);content:'\f0c4';font-size:20px;line-height:1;transition:left 1s ease-in-out, color 0.3s ease, border-color 0.3s ease}.single hr.awesome-hr:hover::before{left:calc(95% - 20px)}.single .content{margin-block:1rem}.single .content>h1,.single .content>h2{font-size:1.5em;padding-bottom:0.3em;border-bottom:1px solid #f0f0f0}.single .content>h3{font-size:1.25em}.single .content>h4{font-size:1em}.single .content>h5{font-size:0.875em}.single .content>h6{font-size:0.85em}.single .content>.heading-element{display:flex;font-weight:bold;line-height:1.25;margin-top:1.5rem;margin-bottom:1rem}[data-theme='dark'] .single .content>.heading-element{font-weight:bolder}.single .content>.heading-element code{padding:0 .2em;font-size:inherit}.single .content>.heading-element:hover>.heading-mark,.single .content>.heading-element:target>.heading-mark{opacity:1}.single .content>.heading-element>.heading-mark{display:inline-flex;align-items:center;opacity:0;padding-inline:0.5rem;transition:all 0.2s ease-in-out}.single .content>.heading-element>.heading-mark a,.single .content>.heading-element>.heading-mark a::before,.single .content>.heading-element>.heading-mark a::after{text-decoration:none;color:#2376b7}[data-theme='dark'] .single .content>.heading-element>.heading-mark a,[data-theme='dark'] .single .content>.heading-element>.heading-mark a::before,[data-theme='dark'] .single .content>.heading-element>.heading-mark a::after{color:#1781b5}.single .content>.heading-element>.heading-mark a:active,.single .content>.heading-element>.heading-mark a:hover{color:#ea517f}[data-theme='dark'] .single .content>.heading-element>.heading-mark a:active,[data-theme='dark'] .single .content>.heading-element>.heading-mark a:hover{color:#cc5595}.single .content>.heading-element>.heading-mark svg{fill:currentColor}.single .content p{margin:0.5rem 0}.single .content b,.single .content strong{font-weight:bold}[data-theme='dark'] .single .content b,[data-theme='dark'] .single .content strong{color:#ddd}.single .content a,.single .content a::before,.single .content a::after{text-decoration:none;color:#2376b7}[data-theme='dark'] .single .content a,[data-theme='dark'] .single .content a::before,[data-theme='dark'] .single .content a::after{color:#1781b5}.single .content a:active,.single .content a:hover{color:#ea517f}[data-theme='dark'] .single .content a:active,[data-theme='dark'] .single .content a:hover{color:#cc5595}.single .content a{white-space:normal;word-wrap:break-word;overflow-wrap:break-word}[data-theme='dark'] .single .content a b,[data-theme='dark'] .single .content a strong{color:#1781b5}.single .content [data-theme='dark'] a:hover b,.single .content [data-theme='dark'] a:hover strong{color:#cc5595}.single .content ul,.single .content ol,.single .content dl{margin-block:0.5em}.single .content ul{list-style-type:disc}.single .content ul li[data-task]{list-style:none;margin-left:-1.5rem}.single .content li[data-task]{color:var(--fi-task-color)}.single .content li[data-task] .checkbox-icon{margin-right:0.25em;color:var(--fi-checkbox-color)}.single .content li[data-task='x'],.single .content li[data-task='-']{--fi-task-color: var(--fi-secondary);text-decoration:line-through rgba(145,145,145,0.6)}.single .content li[data-task='x']{--fi-checkbox-color: var(--fi-primary)}.single .content li[data-task='-']{opacity:0.6}.single .content li[data-task='/']{--fi-task-color: var(--fi-success)}.single .content li[data-task='!']{--fi-checkbox-color: var(--fi-danger)}.single .content li[data-task='?']{--fi-checkbox-color: var(--fi-warning)}.single .content dl dt{font-weight:bold}.single .content dl dd{margin:0.25em 0 1em}.single .content ruby{background:#f6f8fa}.single .content ruby rt{color:#b1b1ba}[data-theme='dark'] .single .content ruby{background:#2d333b}[data-theme='dark'] .single .content ruby rt{color:#909092}.single .content mark{padding:0.14em 0.28em;background-color:var(--fi-mark-background-color, mark);color:var(--fi-mark-color, marktext);-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.single .content mark.mark-primary{--fi-mark-color: #fff;--fi-mark-background-color: var(--fi-primary)}.single .content mark.mark-secondary{--fi-mark-color: #fff;--fi-mark-background-color: var(--fi-secondary)}.single .content mark.mark-success{--fi-mark-color: #fff;--fi-mark-background-color: var(--fi-success)}.single .content mark.mark-info{--fi-mark-color: #fff;--fi-mark-background-color: var(--fi-info)}.single .content mark.mark-warning{--fi-mark-color: #fff;--fi-mark-background-color: var(--fi-warning)}.single .content mark.mark-danger{--fi-mark-color: #fff;--fi-mark-background-color: var(--fi-danger)}.single .content .table-wrapper{position:relative;overflow-x:auto;--fi-scrollbar-thumb-color: #fff}[data-theme='dark'] .single .content .table-wrapper{--fi-scrollbar-thumb-color: #272c34}.single .content .table-wrapper>table{width:100%;max-width:100%;margin:0.625rem 0;border-spacing:0;background:#fff;border-collapse:collapse}[data-theme='dark'] .single .content .table-wrapper>table{background:#272c34}.single .content .table-wrapper>table thead{background:#ededed}[data-theme='dark'] .single .content .table-wrapper>table thead{background-color:#20252b}.single .content .table-wrapper>table:not([class]) tbody tr:nth-child(odd){background:#f9f9f9}[data-theme='dark'] .single .content .table-wrapper>table:not([class]) tbody tr:nth-child(odd){background-color:#2d333c}.single .content .table-wrapper>table:not([class]) tbody tr:hover{background:#f4f4f4}[data-theme='dark'] .single .content .table-wrapper>table:not([class]) tbody tr:hover{background-color:#333a44}.single .content .table-wrapper>table th,.single .content .table-wrapper>table td{padding:0.3rem 1rem;border:1px solid #e8e8e8}[data-theme='dark'] .single .content .table-wrapper>table th,[data-theme='dark'] .single .content .table-wrapper>table td{border-color:#1c2025}.single .content img{max-width:100%}.single .content img:not([src*='.svg']){min-height:1em}.single .content img.version{height:1.25em;vertical-align:text-bottom}.single .content svg{max-width:100%}.single .content figure{margin:0.5rem;text-align:center}.single .content figure .image-caption:not(:empty){min-width:20%;max-width:80%;display:inline-block;padding:0.5rem;margin:0 auto;font-size:0.875rem;color:#969696}.single .content figure img{display:block;margin:0 auto;height:auto;overflow:hidden}.single .content blockquote{display:block;border-left:0.25em solid #f0f0f0;color:#697681;padding:0 1em;margin:1rem 0}.single .content blockquote ul,.single .content blockquote ol{padding-left:1.5em}.single .content blockquote dl dd{margin-inline-start:0.25em}[data-theme='dark'] .single .content blockquote{color:#9ba3aa;border-left-color:#363636}.single .content blockquote a,.single .content blockquote a::before,.single .content blockquote a::after{text-decoration:none;color:#2376b7}[data-theme='dark'] .single .content blockquote a,[data-theme='dark'] .single .content blockquote a::before,[data-theme='dark'] .single .content blockquote a::after{color:#b1b1ba}.single .content blockquote a:active,.single .content blockquote a:hover{color:#ea517f}[data-theme='dark'] .single .content blockquote a:active,[data-theme='dark'] .single .content blockquote a:hover{color:#fff}.single .content sup[id^='fnref']:has(.footnote-ref){padding:2px 4px;background-color:rgba(238,238,238,0.7);color:#555555;border-radius:4px;cursor:pointer}[data-theme='dark'] .single .content sup[id^='fnref']:has(.footnote-ref){background-color:rgba(56,139,253,0.1);color:#58a6ff}.single .content sup[id^='fnref']:has(.footnote-ref) a,.single .content sup[id^='fnref']:has(.footnote-ref) a::before,.single .content sup[id^='fnref']:has(.footnote-ref) a::after{text-decoration:none;color:#161209}[data-theme='dark'] .single .content sup[id^='fnref']:has(.footnote-ref) a,[data-theme='dark'] .single .content sup[id^='fnref']:has(.footnote-ref) a::before,[data-theme='dark'] .single .content sup[id^='fnref']:has(.footnote-ref) a::after{color:#1781b5}.single .content sup[id^='fnref']:has(.footnote-ref) a:active,.single .content sup[id^='fnref']:has(.footnote-ref) a:hover{color:#2983bb}[data-theme='dark'] .single .content sup[id^='fnref']:has(.footnote-ref) a:active,[data-theme='dark'] .single .content sup[id^='fnref']:has(.footnote-ref) a:hover{color:#cc5595}.single .content .footnotes{font-size:0.75rem;white-space:normal;color:#a3a3ae}.single .content .footnotes ol{margin-block:1em;padding-left:1rem}.single .content .footnotes hr{background-color:var(--fi-hr-background-color);height:1px;margin-block:1em}.single .content .footnotes code{font-size:0.75rem}.single .content .footnotes a:not(.footnote-backref){color:inherit;text-decoration:underline}.single .content .footnotes a,.single .content .footnotes a::before,.single .content .footnotes a::after{text-decoration:none;color:#2376b7}[data-theme='dark'] .single .content .footnotes a,[data-theme='dark'] .single .content .footnotes a::before,[data-theme='dark'] .single .content .footnotes a::after{color:#b1b1ba}.single .content .footnotes a:active,.single .content .footnotes a:hover{color:#ea517f}[data-theme='dark'] .single .content .footnotes a:active,[data-theme='dark'] .single .content .footnotes a:hover{color:#fff}[data-theme='dark'] .single .content .footnotes{color:#9d9d9f}.single .content .footnotes p{margin:0.25rem 0}.single .content kbd{display:inline-block;padding:0 0.25rem;background-color:#fff;border:1px solid #f0f0f0;font-size:0.8rem;font-family:Source Code Pro,Menlo,Consolas,Monaco,monospace,system-ui,-apple-system,BlinkMacSystemFont,PingFang SC,Microsoft YaHei UI,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,Helvetica,Arial,sans-serif;color:#24292f;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;-webkit-box-shadow:inset 0 -1px 0 #f0f0f0;box-shadow:inset 0 -1px 0 #f0f0f0}[data-theme='dark'] .single .content kbd{background-color:#292a2e;border:1px solid #363636;color:#adbac7;-webkit-box-shadow:inset 0 -1px 0 #363636;box-shadow:inset 0 -1px 0 #363636}.single .content .katex-display{max-width:100%;overflow-x:auto;padding-block:2px}.single .content .alert{padding:0.5rem 1rem;margin-block:1rem;color:inherit;border-left:0.25em solid var(--fi-alert-border-color)}.single .content .alert>:first-child{margin-top:0}.single .content .alert>:last-child{margin-bottom:0}.single .content .alert .alert-title{display:flex;font-weight:500;align-items:center;line-height:1;margin-bottom:1rem;color:var(--fi-alert-color)}.single .content .alert .alert-title>svg.icon{width:initial;height:initial;margin-right:0.5rem}.single .content .alert.alert-note{--fi-alert-border-color: #0969da;--fi-alert-color: #0969da}.single .content .alert.alert-tip{--fi-alert-border-color: #1a7f37;--fi-alert-color: #1a7f37}.single .content .alert.alert-important{--fi-alert-border-color: #8250df;--fi-alert-color: #8250df}.single .content .alert.alert-warning{--fi-alert-border-color: #bf8700;--fi-alert-color: #9a6700}.single .content .alert.alert-caution{--fi-alert-border-color: #cf222e;--fi-alert-color: #d1242f}[data-theme='dark'] .single .content .alert.alert-note{--fi-alert-border-color: #316dca;--fi-alert-color: #478be6}[data-theme='dark'] .single .content .alert.alert-tip{--fi-alert-border-color: #347d39;--fi-alert-color: #57ab5a}[data-theme='dark'] .single .content .alert.alert-important{--fi-alert-border-color: #8256d0;--fi-alert-color: #986ee2}[data-theme='dark'] .single .content .alert.alert-warning{--fi-alert-border-color: #966600;--fi-alert-color: #c69026}[data-theme='dark'] .single .content .alert.alert-caution{--fi-alert-border-color: #c93c37;--fi-alert-color: #e5534b}.single .content code{padding:0.2em 0.4em;margin:0;font-size:.875rem;font-family:Source Code Pro,Menlo,Consolas,Monaco,monospace,system-ui,-apple-system,BlinkMacSystemFont,PingFang SC,Microsoft YaHei UI,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,Helvetica,Arial,sans-serif;color:#24292f;background-color:rgba(175,184,193,0.2);text-decoration:inherit;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;word-wrap:break-word;overflow-wrap:break-word;-webkit-line-break:auto;-ms-line-break:auto;line-break:auto}[data-theme='dark'] .single .content code{color:#adbac7;background-color:rgba(99,110,123,0.4)}.single .content pre:not(.mermaid[data-processed='true']){margin:0;line-height:1.45em;padding:0.5rem;overflow:auto;font-size:.875rem;font-family:Source Code Pro,Menlo,Consolas,Monaco,monospace,system-ui,-apple-system,BlinkMacSystemFont,PingFang SC,Microsoft YaHei UI,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,Helvetica,Arial,sans-serif;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;-moz-tab-size:4;-o-tab-size:4;tab-size:4;background-color:#f6f8fa}[data-theme='dark'] .single .content pre:not(.mermaid[data-processed='true']){background-color:#2d333b}.single .content pre:not(.mermaid[data-processed='true']) code{padding:0;background-color:transparent;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;min-width:-webkit-max-content;min-width:-moz-max-content;min-width:intrinsic;min-width:max-content}[data-theme='dark'] .single .content pre:not(.mermaid[data-processed='true']) code{background-color:transparent}.single .content pre:not(.mermaid[data-processed='true']) img{min-height:1em;max-height:1.2em;vertical-align:text-bottom}.single .content .highlight,.single .content .gist{font-size:.875rem;font-family:Source Code Pro,Menlo,Consolas,Monaco,monospace,system-ui,-apple-system,BlinkMacSystemFont,PingFang SC,Microsoft YaHei UI,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,Helvetica,Arial,sans-serif}.single .content .highlight .table-wrapper,.single .content .gist .table-wrapper{--fi-scrollbar-thumb-color: initial}.single .content .highlight .table-wrapper>table,.single .content .highlight .table-wrapper>table thead,.single .content .highlight .table-wrapper>table tr,.single .content .highlight .table-wrapper>table td,.single .content .gist .table-wrapper>table,.single .content .gist .table-wrapper>table thead,.single .content .gist .table-wrapper>table tr,.single .content .gist .table-wrapper>table td{margin:0;padding:0;border:none !important;white-space:nowrap}.single .content .highlight{margin:0.5rem 0}.single .content .highlight>.chroma{position:relative}.single .content .highlight>.chroma .code-header{--fi-bg-code-header: #ecf0f5;display:flex;justify-content:space-between;align-items:center;box-sizing:border-box;width:100%;font-family:system-ui,-apple-system,BlinkMacSystemFont,PingFang SC,Microsoft YaHei UI,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,Helvetica,Arial,sans-serif;font-weight:bold;line-height:1.4em;color:#9c9c9c;background-color:var(--fi-bg-code-header);-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;-webkit-transition:border-radius 0.2s ease;-moz-transition:border-radius 0.2s ease;-o-transition:border-radius 0.2s ease;transition:border-radius 0.2s ease}.single .content .highlight>.chroma .code-header+.table-wrapper{max-height:0;-webkit-border-radius:0 0 5px 5px;-moz-border-radius:0 0 5px 5px;border-radius:0 0 5px 5px;-webkit-transition:max-height 0.2s cubic-bezier(0, 1, 0, 1) -0.1s;-moz-transition:max-height 0.2s cubic-bezier(0, 1, 0, 1) -0.1s;-o-transition:max-height 0.2s cubic-bezier(0, 1, 0, 1) -0.1s;transition:max-height 0.2s cubic-bezier(0, 1, 0, 1) -0.1s}[data-theme='dark'] .single .content .highlight>.chroma .code-header{color:#b1b0b0;--fi-bg-code-header: #262b32}.single .content .highlight>.chroma .code-header:hover{cursor:pointer}.single .content .highlight>.chroma .code-header .code-title{width:100%;padding:0.4rem}.single .content .highlight>.chroma .code-header .code-title .arrow{padding:0 0.2rem;-webkit-transition:transform 0.2s ease;-moz-transition:transform 0.2s ease;-o-transition:transform 0.2s ease;transition:transform 0.2s ease}.single .content .highlight>.chroma .code-header .code-title .title-inner{position:absolute;left:50%;transform:translateX(-50%);background-color:var(--fi-bg-code-header);padding-inline:1em;max-width:80%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;z-index:1}.single .content .highlight>.chroma .code-header .code-title::after{padding-left:0.2rem;content:'Code'}.single .content .highlight>.chroma .code-header .ellipses{padding:0.4rem}.single .content .highlight>.chroma .code-header .edit,.single .content .highlight>.chroma .code-header .copy{display:none;padding:0.4rem}.single .content .highlight>.chroma .code-header .edit:hover,.single .content .highlight>.chroma .code-header .copy:hover{cursor:pointer;color:#2983bb}[data-theme='dark'] .single .content .highlight>.chroma .code-header .edit:hover,[data-theme='dark'] .single .content .highlight>.chroma .code-header .copy:hover{color:#fff}.single .content .highlight>.chroma .code-header.language-abap .code-title::after{content:"ABAP"}.single .content .highlight>.chroma .code-header.language-abnf .code-title::after{content:"ABNF"}.single .content .highlight>.chroma .code-header.language-as .code-title::after{content:"ActionScript"}.single .content .highlight>.chroma .code-header.language-actionscript .code-title::after{content:"ActionScript"}.single .content .highlight>.chroma .code-header.language-as3 .code-title::after{content:"ActionScript 3"}.single .content .highlight>.chroma .code-header.language-actionscript3 .code-title::after{content:"ActionScript 3"}.single .content .highlight>.chroma .code-header.language-ada .code-title::after{content:"Ada"}.single .content .highlight>.chroma .code-header.language-ada95 .code-title::after{content:"Ada"}.single .content .highlight>.chroma .code-header.language-ada2005 .code-title::after{content:"Ada"}.single .content .highlight>.chroma .code-header.language-al .code-title::after{content:"AL"}.single .content .highlight>.chroma .code-header.language-ng2 .code-title::after{content:"Angular2"}.single .content .highlight>.chroma .code-header.language-antlr .code-title::after{content:"ANTLR"}.single .content .highlight>.chroma .code-header.language-apacheconf .code-title::after{content:"ApacheConf"}.single .content .highlight>.chroma .code-header.language-aconf .code-title::after{content:"ApacheConf"}.single .content .highlight>.chroma .code-header.language-apache .code-title::after{content:"ApacheConf"}.single .content .highlight>.chroma .code-header.language-apl .code-title::after{content:"APL"}.single .content .highlight>.chroma .code-header.language-applescript .code-title::after{content:"AppleScript"}.single .content .highlight>.chroma .code-header.language-arduino .code-title::after{content:"Arduino"}.single .content .highlight>.chroma .code-header.language-armasm .code-title::after{content:"ArmAsm"}.single .content .highlight>.chroma .code-header.language-awk .code-title::after{content:"Awk"}.single .content .highlight>.chroma .code-header.language-gawk .code-title::after{content:"Awk"}.single .content .highlight>.chroma .code-header.language-mawk .code-title::after{content:"Awk"}.single .content .highlight>.chroma .code-header.language-nawk .code-title::after{content:"Awk"}.single .content .highlight>.chroma .code-header.language-ballerina .code-title::after{content:"Ballerina"}.single .content .highlight>.chroma .code-header.language-make .code-title::after{content:"Base Makefile"}.single .content .highlight>.chroma .code-header.language-makefile .code-title::after{content:"Base Makefile"}.single .content .highlight>.chroma .code-header.language-mf .code-title::after{content:"Base Makefile"}.single .content .highlight>.chroma .code-header.language-bsdmake .code-title::after{content:"Base Makefile"}.single .content .highlight>.chroma .code-header.language-bash .code-title::after{content:"Bash"}.single .content .highlight>.chroma .code-header.language-sh .code-title::after{content:"Bash"}.single .content .highlight>.chroma .code-header.language-ksh .code-title::after{content:"Bash"}.single .content .highlight>.chroma .code-header.language-zsh .code-title::after{content:"Bash"}.single .content .highlight>.chroma .code-header.language-shell .code-title::after{content:"Bash"}.single .content .highlight>.chroma .code-header.language-bash-session .code-title::after{content:"BashSession"}.single .content .highlight>.chroma .code-header.language-console .code-title::after{content:"BashSession"}.single .content .highlight>.chroma .code-header.language-shell-session .code-title::after{content:"BashSession"}.single .content .highlight>.chroma .code-header.language-bat .code-title::after{content:"Batchfile"}.single .content .highlight>.chroma .code-header.language-batch .code-title::after{content:"Batchfile"}.single .content .highlight>.chroma .code-header.language-dosbatch .code-title::after{content:"Batchfile"}.single .content .highlight>.chroma .code-header.language-winbatch .code-title::after{content:"Batchfile"}.single .content .highlight>.chroma .code-header.language-bib .code-title::after{content:"BibTeX"}.single .content .highlight>.chroma .code-header.language-bibtex .code-title::after{content:"BibTeX"}.single .content .highlight>.chroma .code-header.language-bicep .code-title::after{content:"Bicep"}.single .content .highlight>.chroma .code-header.language-blitzbasic .code-title::after{content:"BlitzBasic"}.single .content .highlight>.chroma .code-header.language-b3d .code-title::after{content:"BlitzBasic"}.single .content .highlight>.chroma .code-header.language-bplus .code-title::after{content:"BlitzBasic"}.single .content .highlight>.chroma .code-header.language-bnf .code-title::after{content:"BNF"}.single .content .highlight>.chroma .code-header.language-brainfuck .code-title::after{content:"Brainfuck"}.single .content .highlight>.chroma .code-header.language-bf .code-title::after{content:"Brainfuck"}.single .content .highlight>.chroma .code-header.language-c .code-title::after{content:"C"}.single .content .highlight>.chroma .code-header.language-csharp .code-title::after{content:"C#"}.single .content .highlight>.chroma .code-header.language-c\# .code-title::after{content:"C#"}.single .content .highlight>.chroma .code-header.language-cpp .code-title::after{content:"C++"}.single .content .highlight>.chroma .code-header.language-c\+\+ .code-title::after{content:"C++"}.single .content .highlight>.chroma .code-header.language-caddyfile .code-title::after{content:"Caddyfile"}.single .content .highlight>.chroma .code-header.language-caddy .code-title::after{content:"Caddyfile"}.single .content .highlight>.chroma .code-header.language-caddyfile-directives .code-title::after{content:"Caddyfile Directives"}.single .content .highlight>.chroma .code-header.language-caddyfile-d .code-title::after{content:"Caddyfile Directives"}.single .content .highlight>.chroma .code-header.language-caddy-d .code-title::after{content:"Caddyfile Directives"}.single .content .highlight>.chroma .code-header.language-capnp .code-title::after{content:"Cap'n Proto"}.single .content .highlight>.chroma .code-header.language-cassandra .code-title::after{content:"Cassandra CQL"}.single .content .highlight>.chroma .code-header.language-cql .code-title::after{content:"Cassandra CQL"}.single .content .highlight>.chroma .code-header.language-ceylon .code-title::after{content:"Ceylon"}.single .content .highlight>.chroma .code-header.language-cfengine3 .code-title::after{content:"CFEngine3"}.single .content .highlight>.chroma .code-header.language-cf3 .code-title::after{content:"CFEngine3"}.single .content .highlight>.chroma .code-header.language-cfs .code-title::after{content:"cfstatement"}.single .content .highlight>.chroma .code-header.language-chai .code-title::after{content:"ChaiScript"}.single .content .highlight>.chroma .code-header.language-chaiscript .code-title::after{content:"ChaiScript"}.single .content .highlight>.chroma .code-header.language-cheetah .code-title::after{content:"Cheetah"}.single .content .highlight>.chroma .code-header.language-spitfire .code-title::after{content:"Cheetah"}.single .content .highlight>.chroma .code-header.language-clojure .code-title::after{content:"Clojure"}.single .content .highlight>.chroma .code-header.language-clj .code-title::after{content:"Clojure"}.single .content .highlight>.chroma .code-header.language-cmake .code-title::after{content:"CMake"}.single .content .highlight>.chroma .code-header.language-cobol .code-title::after{content:"COBOL"}.single .content .highlight>.chroma .code-header.language-coffee-script .code-title::after{content:"CoffeeScript"}.single .content .highlight>.chroma .code-header.language-coffeescript .code-title::after{content:"CoffeeScript"}.single .content .highlight>.chroma .code-header.language-coffee .code-title::after{content:"CoffeeScript"}.single .content .highlight>.chroma .code-header.language-common-lisp .code-title::after{content:"Common Lisp"}.single .content .highlight>.chroma .code-header.language-cl .code-title::after{content:"Common Lisp"}.single .content .highlight>.chroma .code-header.language-lisp .code-title::after{content:"Common Lisp"}.single .content .highlight>.chroma .code-header.language-coq .code-title::after{content:"Coq"}.single .content .highlight>.chroma .code-header.language-cr .code-title::after{content:"Crystal"}.single .content .highlight>.chroma .code-header.language-crystal .code-title::after{content:"Crystal"}.single .content .highlight>.chroma .code-header.language-css .code-title::after{content:"CSS"}.single .content .highlight>.chroma .code-header.language-cython .code-title::after{content:"Cython"}.single .content .highlight>.chroma .code-header.language-pyx .code-title::after{content:"Cython"}.single .content .highlight>.chroma .code-header.language-pyrex .code-title::after{content:"Cython"}.single .content .highlight>.chroma .code-header.language-d .code-title::after{content:"D"}.single .content .highlight>.chroma .code-header.language-dart .code-title::after{content:"Dart"}.single .content .highlight>.chroma .code-header.language-diff .code-title::after{content:"Diff"}.single .content .highlight>.chroma .code-header.language-udiff .code-title::after{content:"Diff"}.single .content .highlight>.chroma .code-header.language-django .code-title::after{content:"Django/Jinja"}.single .content .highlight>.chroma .code-header.language-jinja .code-title::after{content:"Django/Jinja"}.single .content .highlight>.chroma .code-header.language-docker .code-title::after{content:"Docker"}.single .content .highlight>.chroma .code-header.language-dockerfile .code-title::after{content:"Docker"}.single .content .highlight>.chroma .code-header.language-dtd .code-title::after{content:"DTD"}.single .content .highlight>.chroma .code-header.language-dylan .code-title::after{content:"Dylan"}.single .content .highlight>.chroma .code-header.language-ebnf .code-title::after{content:"EBNF"}.single .content .highlight>.chroma .code-header.language-elixir .code-title::after{content:"Elixir"}.single .content .highlight>.chroma .code-header.language-ex .code-title::after{content:"Elixir"}.single .content .highlight>.chroma .code-header.language-exs .code-title::after{content:"Elixir"}.single .content .highlight>.chroma .code-header.language-elm .code-title::after{content:"Elm"}.single .content .highlight>.chroma .code-header.language-emacs .code-title::after{content:"EmacsLisp"}.single .content .highlight>.chroma .code-header.language-elisp .code-title::after{content:"EmacsLisp"}.single .content .highlight>.chroma .code-header.language-emacs-lisp .code-title::after{content:"EmacsLisp"}.single .content .highlight>.chroma .code-header.language-erlang .code-title::after{content:"Erlang"}.single .content .highlight>.chroma .code-header.language-factor .code-title::after{content:"Factor"}.single .content .highlight>.chroma .code-header.language-fennel .code-title::after{content:"Fennel"}.single .content .highlight>.chroma .code-header.language-fnl .code-title::after{content:"Fennel"}.single .content .highlight>.chroma .code-header.language-fish .code-title::after{content:"Fish"}.single .content .highlight>.chroma .code-header.language-fishshell .code-title::after{content:"Fish"}.single .content .highlight>.chroma .code-header.language-forth .code-title::after{content:"Forth"}.single .content .highlight>.chroma .code-header.language-fortran .code-title::after{content:"Fortran"}.single .content .highlight>.chroma .code-header.language-fortranfixed .code-title::after{content:"FortranFixed"}.single .content .highlight>.chroma .code-header.language-fsharp .code-title::after{content:"FSharp"}.single .content .highlight>.chroma .code-header.language-gas .code-title::after{content:"GAS"}.single .content .highlight>.chroma .code-header.language-asm .code-title::after{content:"GAS"}.single .content .highlight>.chroma .code-header.language-gdscript .code-title::after{content:"GDScript"}.single .content .highlight>.chroma .code-header.language-gd .code-title::after{content:"GDScript"}.single .content .highlight>.chroma .code-header.language-genshi .code-title::after{content:"Genshi"}.single .content .highlight>.chroma .code-header.language-kid .code-title::after{content:"Genshi"}.single .content .highlight>.chroma .code-header.language-xml\+genshi .code-title::after{content:"Genshi"}.single .content .highlight>.chroma .code-header.language-xml\+kid .code-title::after{content:"Genshi"}.single .content .highlight>.chroma .code-header.language-html\+genshi .code-title::after{content:"Genshi HTML"}.single .content .highlight>.chroma .code-header.language-html\+kid .code-title::after{content:"Genshi HTML"}.single .content .highlight>.chroma .code-header.language-genshitext .code-title::after{content:"Genshi Text"}.single .content .highlight>.chroma .code-header.language-cucumber .code-title::after{content:"Gherkin"}.single .content .highlight>.chroma .code-header.language-Cucumber .code-title::after{content:"Gherkin"}.single .content .highlight>.chroma .code-header.language-gherkin .code-title::after{content:"Gherkin"}.single .content .highlight>.chroma .code-header.language-Gherkin .code-title::after{content:"Gherkin"}.single .content .highlight>.chroma .code-header.language-glsl .code-title::after{content:"GLSL"}.single .content .highlight>.chroma .code-header.language-gnuplot .code-title::after{content:"Gnuplot"}.single .content .highlight>.chroma .code-header.language-go .code-title::after{content:"Go"}.single .content .highlight>.chroma .code-header.language-golang .code-title::after{content:"Go"}.single .content .highlight>.chroma .code-header.language-go-html-template .code-title::after{content:"Go HTML Template"}.single .content .highlight>.chroma .code-header.language-go-text-template .code-title::after{content:"Go Text Template"}.single .content .highlight>.chroma .code-header.language-graphql .code-title::after{content:"GraphQL"}.single .content .highlight>.chroma .code-header.language-graphqls .code-title::after{content:"GraphQL"}.single .content .highlight>.chroma .code-header.language-gql .code-title::after{content:"GraphQL"}.single .content .highlight>.chroma .code-header.language-groff .code-title::after{content:"Groff"}.single .content .highlight>.chroma .code-header.language-nroff .code-title::after{content:"Groff"}.single .content .highlight>.chroma .code-header.language-man .code-title::after{content:"Groff"}.single .content .highlight>.chroma .code-header.language-groovy .code-title::after{content:"Groovy"}.single .content .highlight>.chroma .code-header.language-handlebars .code-title::after{content:"Handlebars"}.single .content .highlight>.chroma .code-header.language-hbs .code-title::after{content:"Handlebars"}.single .content .highlight>.chroma .code-header.language-haskell .code-title::after{content:"Haskell"}.single .content .highlight>.chroma .code-header.language-hs .code-title::after{content:"Haskell"}.single .content .highlight>.chroma .code-header.language-hx .code-title::after{content:"Haxe"}.single .content .highlight>.chroma .code-header.language-haxe .code-title::after{content:"Haxe"}.single .content .highlight>.chroma .code-header.language-hxsl .code-title::after{content:"Haxe"}.single .content .highlight>.chroma .code-header.language-hcl .code-title::after{content:"HCL"}.single .content .highlight>.chroma .code-header.language-hexdump .code-title::after{content:"Hexdump"}.single .content .highlight>.chroma .code-header.language-hlb .code-title::after{content:"HLB"}.single .content .highlight>.chroma .code-header.language-html .code-title::after{content:"HTML"}.single .content .highlight>.chroma .code-header.language-http .code-title::after{content:"HTTP"}.single .content .highlight>.chroma .code-header.language-hylang .code-title::after{content:"Hy"}.single .content .highlight>.chroma .code-header.language-idris .code-title::after{content:"Idris"}.single .content .highlight>.chroma .code-header.language-idr .code-title::after{content:"Idris"}.single .content .highlight>.chroma .code-header.language-igor .code-title::after{content:"Igor"}.single .content .highlight>.chroma .code-header.language-igorpro .code-title::after{content:"Igor"}.single .content .highlight>.chroma .code-header.language-ini .code-title::after{content:"INI"}.single .content .highlight>.chroma .code-header.language-cfg .code-title::after{content:"INI"}.single .content .highlight>.chroma .code-header.language-dosini .code-title::after{content:"INI"}.single .content .highlight>.chroma .code-header.language-io .code-title::after{content:"Io"}.single .content .highlight>.chroma .code-header.language-j .code-title::after{content:"J"}.single .content .highlight>.chroma .code-header.language-java .code-title::after{content:"Java"}.single .content .highlight>.chroma .code-header.language-js .code-title::after{content:"JavaScript"}.single .content .highlight>.chroma .code-header.language-javascript .code-title::after{content:"JavaScript"}.single .content .highlight>.chroma .code-header.language-json .code-title::after{content:"JSON"}.single .content .highlight>.chroma .code-header.language-julia .code-title::after{content:"Julia"}.single .content .highlight>.chroma .code-header.language-jl .code-title::after{content:"Julia"}.single .content .highlight>.chroma .code-header.language-jungle .code-title::after{content:"Jungle"}.single .content .highlight>.chroma .code-header.language-kotlin .code-title::after{content:"Kotlin"}.single .content .highlight>.chroma .code-header.language-lighty .code-title::after{content:"Lighttpd configuration file"}.single .content .highlight>.chroma .code-header.language-lighttpd .code-title::after{content:"Lighttpd configuration file"}.single .content .highlight>.chroma .code-header.language-llvm .code-title::after{content:"LLVM"}.single .content .highlight>.chroma .code-header.language-lua .code-title::after{content:"Lua"}.single .content .highlight>.chroma .code-header.language-mako .code-title::after{content:"Mako"}.single .content .highlight>.chroma .code-header.language-md .code-title::after{content:"Markdown"}.single .content .highlight>.chroma .code-header.language-mkd .code-title::after{content:"Markdown"}.single .content .highlight>.chroma .code-header.language-mason .code-title::after{content:"Mason"}.single .content .highlight>.chroma .code-header.language-mathematica .code-title::after{content:"Mathematica"}.single .content .highlight>.chroma .code-header.language-mma .code-title::after{content:"Mathematica"}.single .content .highlight>.chroma .code-header.language-nb .code-title::after{content:"Mathematica"}.single .content .highlight>.chroma .code-header.language-matlab .code-title::after{content:"Matlab"}.single .content .highlight>.chroma .code-header.language-mcfunction .code-title::after{content:"mcfunction"}.single .content .highlight>.chroma .code-header.language-meson .code-title::after{content:"Meson"}.single .content .highlight>.chroma .code-header.language-meson\.build .code-title::after{content:"Meson"}.single .content .highlight>.chroma .code-header.language-metal .code-title::after{content:"Metal"}.single .content .highlight>.chroma .code-header.language-minizinc .code-title::after{content:"MiniZinc"}.single .content .highlight>.chroma .code-header.language-MZN .code-title::after{content:"MiniZinc"}.single .content .highlight>.chroma .code-header.language-mzn .code-title::after{content:"MiniZinc"}.single .content .highlight>.chroma .code-header.language-mlir .code-title::after{content:"MLIR"}.single .content .highlight>.chroma .code-header.language-modula2 .code-title::after{content:"Modula-2"}.single .content .highlight>.chroma .code-header.language-m2 .code-title::after{content:"Modula-2"}.single .content .highlight>.chroma .code-header.language-monkeyc .code-title::after{content:"MonkeyC"}.single .content .highlight>.chroma .code-header.language-morrowind .code-title::after{content:"MorrowindScript"}.single .content .highlight>.chroma .code-header.language-mwscript .code-title::after{content:"MorrowindScript"}.single .content .highlight>.chroma .code-header.language-myghty .code-title::after{content:"Myghty"}.single .content .highlight>.chroma .code-header.language-mysql .code-title::after{content:"MySQL"}.single .content .highlight>.chroma .code-header.language-mariadb .code-title::after{content:"MySQL"}.single .content .highlight>.chroma .code-header.language-nasm .code-title::after{content:"NASM"}.single .content .highlight>.chroma .code-header.language-newspeak .code-title::after{content:"Newspeak"}.single .content .highlight>.chroma .code-header.language-nginx .code-title::after{content:"Nginx configuration file"}.single .content .highlight>.chroma .code-header.language-nim .code-title::after{content:"Nim"}.single .content .highlight>.chroma .code-header.language-nimrod .code-title::after{content:"Nim"}.single .content .highlight>.chroma .code-header.language-nixos .code-title::after{content:"Nix"}.single .content .highlight>.chroma .code-header.language-nix .code-title::after{content:"Nix"}.single .content .highlight>.chroma .code-header.language-objective-c .code-title::after{content:"Objective-C"}.single .content .highlight>.chroma .code-header.language-objectivec .code-title::after{content:"Objective-C"}.single .content .highlight>.chroma .code-header.language-obj-c .code-title::after{content:"Objective-C"}.single .content .highlight>.chroma .code-header.language-objc .code-title::after{content:"Objective-C"}.single .content .highlight>.chroma .code-header.language-ocaml .code-title::after{content:"OCaml"}.single .content .highlight>.chroma .code-header.language-octave .code-title::after{content:"Octave"}.single .content .highlight>.chroma .code-header.language-ones .code-title::after{content:"OnesEnterprise"}.single .content .highlight>.chroma .code-header.language-onesenterprise .code-title::after{content:"OnesEnterprise"}.single .content .highlight>.chroma .code-header.language-1S .code-title::after{content:"OnesEnterprise"}.single .content .highlight>.chroma .code-header.language-1S:Enterprise .code-title::after{content:"OnesEnterprise"}.single .content .highlight>.chroma .code-header.language-openedge .code-title::after{content:"OpenEdge ABL"}.single .content .highlight>.chroma .code-header.language-abl .code-title::after{content:"OpenEdge ABL"}.single .content .highlight>.chroma .code-header.language-progress .code-title::after{content:"OpenEdge ABL"}.single .content .highlight>.chroma .code-header.language-openedgeabl .code-title::after{content:"OpenEdge ABL"}.single .content .highlight>.chroma .code-header.language-openscad .code-title::after{content:"OpenSCAD"}.single .content .highlight>.chroma .code-header.language-org .code-title::after{content:"Org Mode"}.single .content .highlight>.chroma .code-header.language-orgmode .code-title::after{content:"Org Mode"}.single .content .highlight>.chroma .code-header.language-pacmanconf .code-title::after{content:"PacmanConf"}.single .content .highlight>.chroma .code-header.language-perl .code-title::after{content:"Perl"}.single .content .highlight>.chroma .code-header.language-pl .code-title::after{content:"Perl"}.single .content .highlight>.chroma .code-header.language-php .code-title::after{content:"PHP"}.single .content .highlight>.chroma .code-header.language-php3 .code-title::after{content:"PHP"}.single .content .highlight>.chroma .code-header.language-php4 .code-title::after{content:"PHP"}.single .content .highlight>.chroma .code-header.language-php5 .code-title::after{content:"PHP"}.single .content .highlight>.chroma .code-header.language-phtml .code-title::after{content:"PHTML"}.single .content .highlight>.chroma .code-header.language-pig .code-title::after{content:"Pig"}.single .content .highlight>.chroma .code-header.language-pkgconfig .code-title::after{content:"PkgConfig"}.single .content .highlight>.chroma .code-header.language-plpgsql .code-title::after{content:"PL/pgSQL"}.single .content .highlight>.chroma .code-header.language-text .code-title::after{content:"plaintext"}.single .content .highlight>.chroma .code-header.language-plain .code-title::after{content:"plaintext"}.single .content .highlight>.chroma .code-header.language-no-highlight .code-title::after{content:"plaintext"}.single .content .highlight>.chroma .code-header.language-plutus-core .code-title::after{content:"Plutus Core"}.single .content .highlight>.chroma .code-header.language-plc .code-title::after{content:"Plutus Core"}.single .content .highlight>.chroma .code-header.language-pony .code-title::after{content:"Pony"}.single .content .highlight>.chroma .code-header.language-postgresql .code-title::after{content:"PostgreSQL SQL dialect"}.single .content .highlight>.chroma .code-header.language-postgres .code-title::after{content:"PostgreSQL SQL dialect"}.single .content .highlight>.chroma .code-header.language-postscript .code-title::after{content:"PostScript"}.single .content .highlight>.chroma .code-header.language-postscr .code-title::after{content:"PostScript"}.single .content .highlight>.chroma .code-header.language-pov .code-title::after{content:"POVRay"}.single .content .highlight>.chroma .code-header.language-powerquery .code-title::after{content:"PowerQuery"}.single .content .highlight>.chroma .code-header.language-pq .code-title::after{content:"PowerQuery"}.single .content .highlight>.chroma .code-header.language-powershell .code-title::after{content:"PowerShell"}.single .content .highlight>.chroma .code-header.language-posh .code-title::after{content:"PowerShell"}.single .content .highlight>.chroma .code-header.language-ps1 .code-title::after{content:"PowerShell"}.single .content .highlight>.chroma .code-header.language-psm1 .code-title::after{content:"PowerShell"}.single .content .highlight>.chroma .code-header.language-psd1 .code-title::after{content:"PowerShell"}.single .content .highlight>.chroma .code-header.language-prolog .code-title::after{content:"Prolog"}.single .content .highlight>.chroma .code-header.language-promql .code-title::after{content:"PromQL"}.single .content .highlight>.chroma .code-header.language-protobuf .code-title::after{content:"Protocol Buffer"}.single .content .highlight>.chroma .code-header.language-proto .code-title::after{content:"Protocol Buffer"}.single .content .highlight>.chroma .code-header.language-puppet .code-title::after{content:"Puppet"}.single .content .highlight>.chroma .code-header.language-python .code-title::after{content:"Python"}.single .content .highlight>.chroma .code-header.language-py .code-title::after{content:"Python"}.single .content .highlight>.chroma .code-header.language-sage .code-title::after{content:"Python"}.single .content .highlight>.chroma .code-header.language-python3 .code-title::after{content:"Python"}.single .content .highlight>.chroma .code-header.language-py3 .code-title::after{content:"Python"}.single .content .highlight>.chroma .code-header.language-python2 .code-title::after{content:"Python 2"}.single .content .highlight>.chroma .code-header.language-py2 .code-title::after{content:"Python 2"}.single .content .highlight>.chroma .code-header.language-qbasic .code-title::after{content:"QBasic"}.single .content .highlight>.chroma .code-header.language-basic .code-title::after{content:"QBasic"}.single .content .highlight>.chroma .code-header.language-qml .code-title::after{content:"QML"}.single .content .highlight>.chroma .code-header.language-qbs .code-title::after{content:"QML"}.single .content .highlight>.chroma .code-header.language-splus .code-title::after{content:"R"}.single .content .highlight>.chroma .code-header.language-s .code-title::after{content:"R"}.single .content .highlight>.chroma .code-header.language-r .code-title::after{content:"R"}.single .content .highlight>.chroma .code-header.language-racket .code-title::after{content:"Racket"}.single .content .highlight>.chroma .code-header.language-rkt .code-title::after{content:"Racket"}.single .content .highlight>.chroma .code-header.language-ragel .code-title::after{content:"Ragel"}.single .content .highlight>.chroma .code-header.language-perl6 .code-title::after{content:"Raku"}.single .content .highlight>.chroma .code-header.language-pl6 .code-title::after{content:"Raku"}.single .content .highlight>.chroma .code-header.language-raku .code-title::after{content:"Raku"}.single .content .highlight>.chroma .code-header.language-jsx .code-title::after{content:"react"}.single .content .highlight>.chroma .code-header.language-react .code-title::after{content:"react"}.single .content .highlight>.chroma .code-header.language-reason .code-title::after{content:"ReasonML"}.single .content .highlight>.chroma .code-header.language-reasonml .code-title::after{content:"ReasonML"}.single .content .highlight>.chroma .code-header.language-registry .code-title::after{content:"reg"}.single .content .highlight>.chroma .code-header.language-rst .code-title::after{content:"reStructuredText"}.single .content .highlight>.chroma .code-header.language-rest .code-title::after{content:"reStructuredText"}.single .content .highlight>.chroma .code-header.language-restructuredtext .code-title::after{content:"reStructuredText"}.single .content .highlight>.chroma .code-header.language-rexx .code-title::after{content:"Rexx"}.single .content .highlight>.chroma .code-header.language-arexx .code-title::after{content:"Rexx"}.single .content .highlight>.chroma .code-header.language-rb .code-title::after{content:"Ruby"}.single .content .highlight>.chroma .code-header.language-ruby .code-title::after{content:"Ruby"}.single .content .highlight>.chroma .code-header.language-duby .code-title::after{content:"Ruby"}.single .content .highlight>.chroma .code-header.language-rust .code-title::after{content:"Rust"}.single .content .highlight>.chroma .code-header.language-rs .code-title::after{content:"Rust"}.single .content .highlight>.chroma .code-header.language-sas .code-title::after{content:"SAS"}.single .content .highlight>.chroma .code-header.language-sass .code-title::after{content:"Sass"}.single .content .highlight>.chroma .code-header.language-scala .code-title::after{content:"Scala"}.single .content .highlight>.chroma .code-header.language-scheme .code-title::after{content:"Scheme"}.single .content .highlight>.chroma .code-header.language-scm .code-title::after{content:"Scheme"}.single .content .highlight>.chroma .code-header.language-scilab .code-title::after{content:"Scilab"}.single .content .highlight>.chroma .code-header.language-scss .code-title::after{content:"SCSS"}.single .content .highlight>.chroma .code-header.language-sieve .code-title::after{content:"Sieve"}.single .content .highlight>.chroma .code-header.language-smalltalk .code-title::after{content:"Smalltalk"}.single .content .highlight>.chroma .code-header.language-squeak .code-title::after{content:"Smalltalk"}.single .content .highlight>.chroma .code-header.language-st .code-title::after{content:"Smalltalk"}.single .content .highlight>.chroma .code-header.language-smarty .code-title::after{content:"Smarty"}.single .content .highlight>.chroma .code-header.language-snobol .code-title::after{content:"Snobol"}.single .content .highlight>.chroma .code-header.language-sol .code-title::after{content:"Solidity"}.single .content .highlight>.chroma .code-header.language-solidity .code-title::after{content:"Solidity"}.single .content .highlight>.chroma .code-header.language-sparql .code-title::after{content:"SPARQL"}.single .content .highlight>.chroma .code-header.language-sql .code-title::after{content:"SQL"}.single .content .highlight>.chroma .code-header.language-squidconf .code-title::after{content:"SquidConf"}.single .content .highlight>.chroma .code-header.language-squid\.conf .code-title::after{content:"SquidConf"}.single .content .highlight>.chroma .code-header.language-squid .code-title::after{content:"SquidConf"}.single .content .highlight>.chroma .code-header.language-sml .code-title::after{content:"Standard ML"}.single .content .highlight>.chroma .code-header.language-stylus .code-title::after{content:"Stylus"}.single .content .highlight>.chroma .code-header.language-svelte .code-title::after{content:"Svelte"}.single .content .highlight>.chroma .code-header.language-swift .code-title::after{content:"Swift"}.single .content .highlight>.chroma .code-header.language-systemd .code-title::after{content:"SYSTEMD"}.single .content .highlight>.chroma .code-header.language-systemverilog .code-title::after{content:"systemverilog"}.single .content .highlight>.chroma .code-header.language-sv .code-title::after{content:"systemverilog"}.single .content .highlight>.chroma .code-header.language-tablegen .code-title::after{content:"TableGen"}.single .content .highlight>.chroma .code-header.language-tasm .code-title::after{content:"TASM"}.single .content .highlight>.chroma .code-header.language-tcl .code-title::after{content:"Tcl"}.single .content .highlight>.chroma .code-header.language-tcsh .code-title::after{content:"Tcsh"}.single .content .highlight>.chroma .code-header.language-csh .code-title::after{content:"Tcsh"}.single .content .highlight>.chroma .code-header.language-termcap .code-title::after{content:"Termcap"}.single .content .highlight>.chroma .code-header.language-terminfo .code-title::after{content:"Terminfo"}.single .content .highlight>.chroma .code-header.language-terraform .code-title::after{content:"Terraform"}.single .content .highlight>.chroma .code-header.language-tf .code-title::after{content:"Terraform"}.single .content .highlight>.chroma .code-header.language-tex .code-title::after{content:"TeX"}.single .content .highlight>.chroma .code-header.language-latex .code-title::after{content:"TeX"}.single .content .highlight>.chroma .code-header.language-thrift .code-title::after{content:"Thrift"}.single .content .highlight>.chroma .code-header.language-toml .code-title::after{content:"TOML"}.single .content .highlight>.chroma .code-header.language-tradingview .code-title::after{content:"TradingView"}.single .content .highlight>.chroma .code-header.language-tv .code-title::after{content:"TradingView"}.single .content .highlight>.chroma .code-header.language-tsql .code-title::after{content:"Transact-SQL"}.single .content .highlight>.chroma .code-header.language-t-sql .code-title::after{content:"Transact-SQL"}.single .content .highlight>.chroma .code-header.language-turing .code-title::after{content:"Turing"}.single .content .highlight>.chroma .code-header.language-turtle .code-title::after{content:"Turtle"}.single .content .highlight>.chroma .code-header.language-twig .code-title::after{content:"Twig"}.single .content .highlight>.chroma .code-header.language-ts .code-title::after{content:"TypeScript"}.single .content .highlight>.chroma .code-header.language-tsx .code-title::after{content:"TypeScript"}.single .content .highlight>.chroma .code-header.language-typescript .code-title::after{content:"TypeScript"}.single .content .highlight>.chroma .code-header.language-typoscript .code-title::after{content:"TypoScript"}.single .content .highlight>.chroma .code-header.language-typoscriptcssdata .code-title::after{content:"TypoScriptCssData"}.single .content .highlight>.chroma .code-header.language-typoscripthtmldata .code-title::after{content:"TypoScriptHtmlData"}.single .content .highlight>.chroma .code-header.language-v .code-title::after{content:"verilog"}.single .content .highlight>.chroma .code-header.language-vlang .code-title::after{content:"V"}.single .content .highlight>.chroma .code-header.language-vsh .code-title::after{content:"V shell"}.single .content .highlight>.chroma .code-header.language-vshell .code-title::after{content:"V shell"}.single .content .highlight>.chroma .code-header.language-vb\.net .code-title::after{content:"VB.net"}.single .content .highlight>.chroma .code-header.language-vbnet .code-title::after{content:"VB.net"}.single .content .highlight>.chroma .code-header.language-verilog .code-title::after{content:"verilog"}.single .content .highlight>.chroma .code-header.language-vhdl .code-title::after{content:"VHDL"}.single .content .highlight>.chroma .code-header.language-vim .code-title::after{content:"VimL"}.single .content .highlight>.chroma .code-header.language-vue .code-title::after{content:"vue"}.single .content .highlight>.chroma .code-header.language-vuejs .code-title::after{content:"vue"}.single .content .highlight>.chroma .code-header.language-whiley .code-title::after{content:"Whiley"}.single .content .highlight>.chroma .code-header.language-xml .code-title::after{content:"XML"}.single .content .highlight>.chroma .code-header.language-xorg\.conf .code-title::after{content:"Xorg"}.single .content .highlight>.chroma .code-header.language-yaml .code-title::after{content:"YAML"}.single .content .highlight>.chroma .code-header.language-yang .code-title::after{content:"YANG"}.single .content .highlight>.chroma .code-header.language-zed .code-title::after{content:"Zed"}.single .content .highlight>.chroma .code-header.language-zig .code-title::after{content:"Zig"}.single .content .highlight>.chroma .code-header.language-markdown .code-title::after{content:"Markdown"}.single .content .highlight>.chroma .code-header.language-yml .code-title::after{content:"YAML"}.single .content .highlight>.chroma .table-wrapper{overflow:hidden;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.single .content .highlight>.chroma table{border-spacing:0;width:auto;overflow:auto;display:block;background-color:#f6f8fa}[data-theme='dark'] .single .content .highlight>.chroma table{background-color:#2d333b}.single .content .highlight>.chroma table td{vertical-align:top;padding:0}.single .content .highlight>.chroma table pre{padding:0.25rem;outline-offset:-1px;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.single .content .highlight>.chroma table .ln{min-width:1.25rem}.single .content .highlight>.chroma table .lntd:first-child{min-width:1.6rem;text-align:right;position:sticky;left:0}.single .content .highlight>.chroma table .lntd:first-child pre{-webkit-user-select:none !important;-moz-user-select:none !important;user-select:none !important}.single .content .highlight>.chroma table .lntd:last-child{width:100%}.single .content .highlight>.chroma table .lntd:last-child pre{padding-left:0.5rem;padding-right:0.5rem}.single .content .highlight>.chroma table .lntd:last-child code{width:100%}.single .content .highlight>.chroma.open .code-header{--fi-bg-code-header: #e2e9ef;-webkit-border-radius:5px 5px 0 0;-moz-border-radius:5px 5px 0 0;border-radius:5px 5px 0 0}[data-theme='dark'] .single .content .highlight>.chroma.open .code-header{--fi-bg-code-header: #20242a}.single .content .highlight>.chroma.open .table-wrapper{max-height:100%;-webkit-transition:max-height 0.2s cubic-bezier(0.5, 0, 1, 0) 0s;-moz-transition:max-height 0.2s cubic-bezier(0.5, 0, 1, 0) 0s;-o-transition:max-height 0.2s cubic-bezier(0.5, 0, 1, 0) 0s;transition:max-height 0.2s cubic-bezier(0.5, 0, 1, 0) 0s}.single .content .highlight>.chroma.open .arrow{-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.single .content .highlight>.chroma.open .ellipses{display:none}.single .content .highlight>.chroma.open .edit,.single .content .highlight>.chroma.open .copy{display:inline}.single .content .highlight .hl{display:block;background-color:#fff8c5;box-shadow:-0.5rem 0 0 #fff8c5,0.5rem 0 0 #fff8c5}[data-theme='dark'] .single .content .highlight .hl{background-color:rgba(174,124,20,0.15);box-shadow:-0.5rem 0 0 rgba(174,124,20,0.15),0.5rem 0 0 rgba(174,124,20,0.15)}.single .content .highlight .gl{text-decoration:underline}.single .content .highlight .gs{font-weight:bold}.single .content .highlight .ge{font-style:italic}.single .content .highlight .ln{padding-right:0.5rem;display:inline-block;min-width:1.1rem;text-align:right}.single .content .highlight .lnt,.single .content .highlight .ln{color:#b1b1ba}[data-theme='dark'] .single .content .highlight .lnt,[data-theme='dark'] .single .content .highlight .ln{color:#909092}.single .content .highlight .err{font-style:italic;border-bottom:1px dotted #dc3545}.single .content .highlight .gd{background-color:#ffebe9}[data-theme='dark'] .single .content .highlight .gd{background-color:#78191b}.single .content .highlight .gi{background-color:#dafbe1}[data-theme='dark'] .single .content .highlight .gi{background-color:#1b4721}.single .content .highlight .err{color:#dc3545}.single .content .highlight .k{color:#cf222e}.single .content .highlight .kc{color:#0550ae}.single .content .highlight .kd{color:#cf222e}.single .content .highlight .kn{color:#cf222e}.single .content .highlight .kp{color:#cf222e}.single .content .highlight .kr{color:#cf222e}.single .content .highlight .kt{color:#cf222e}.single .content .highlight .n{color:#24292f}.single .content .highlight .na{color:#0550ae}.single .content .highlight .nb{color:#0550ae}.single .content .highlight .bp{color:#24292f}.single .content .highlight .nc{color:#8250df}.single .content .highlight .no{color:#cf222e}.single .content .highlight .nd{color:#8250df}.single .content .highlight .ni{color:#0550ae}.single .content .highlight .ne{color:#0550ae}.single .content .highlight .nf{color:#0550ae}.single .content .highlight .fm{color:#0550ae}.single .content .highlight .nl{color:#0550ae}.single .content .highlight .nn{color:#8250df}.single .content .highlight .nx{color:#0550ae}.single .content .highlight .py{color:#0550ae}.single .content .highlight .nt{color:#116329}.single .content .highlight .nv{color:#953800}.single .content .highlight .vc{color:#0550ae}.single .content .highlight .vg{color:#0550ae}.single .content .highlight .vi{color:#0550ae}.single .content .highlight .vm{color:#0550ae}.single .content .highlight .l{color:#0550ae}.single .content .highlight .ld{color:#0a3069}.single .content .highlight .s{color:#0a3069}.single .content .highlight .sa{color:#0a3069}.single .content .highlight .sb{color:#0a3069}.single .content .highlight .sc{color:#0a3069}.single .content .highlight .dl{color:#0a3069}.single .content .highlight .sd{color:#0a3069}.single .content .highlight .s2{color:#0a3069}.single .content .highlight .se{color:#0a3069}.single .content .highlight .sh{color:#0a3069}.single .content .highlight .si{color:#0a3069}.single .content .highlight .sx{color:#0a3069}.single .content .highlight .sr{color:#0a3069}.single .content .highlight .s1{color:#0a3069}.single .content .highlight .ss{color:#0a3069}.single .content .highlight .m{color:#0550ae}.single .content .highlight .mb{color:#0550ae}.single .content .highlight .mf{color:#0550ae}.single .content .highlight .mh{color:#0550ae}.single .content .highlight .mi{color:#0550ae}.single .content .highlight .il{color:#0550ae}.single .content .highlight .mo{color:#0550ae}.single .content .highlight .o{color:#cf222e}.single .content .highlight .ow{color:#cf222e}.single .content .highlight .p{color:#24292f}.single .content .highlight .c{color:#6e7781}.single .content .highlight .ch{color:#6e7781}.single .content .highlight .cm{color:#6e7781}.single .content .highlight .c1{color:#6e7781}.single .content .highlight .cs{color:#6e7781}.single .content .highlight .cp{color:#0550ae}.single .content .highlight .cpf{color:#6e7781}.single .content .highlight .g{color:#24292f}.single .content .highlight .gd{color:#82071e}.single .content .highlight .ge{color:#24292f}.single .content .highlight .gr{color:#dc3545}.single .content .highlight .gh{color:#24292f}.single .content .highlight .gi{color:#116329}.single .content .highlight .go{color:#24292f}.single .content .highlight .gp{color:#24292f}.single .content .highlight .gs{color:#24292f}.single .content .highlight .gu{color:#24292f}.single .content .highlight .gt{color:#dc3545}.single .content .highlight .gl{color:#24292f}.single .content .highlight .w{color:#24292f}.single .content .highlight .x{color:#24292f}[data-theme='dark'] .single .content .highlight .err{color:#dc3545}[data-theme='dark'] .single .content .highlight .k{color:#f47067}[data-theme='dark'] .single .content .highlight .kc{color:#6cb6ff}[data-theme='dark'] .single .content .highlight .kd{color:#f47067}[data-theme='dark'] .single .content .highlight .kn{color:#f47067}[data-theme='dark'] .single .content .highlight .kp{color:#f47067}[data-theme='dark'] .single .content .highlight .kr{color:#f47067}[data-theme='dark'] .single .content .highlight .kt{color:#f47067}[data-theme='dark'] .single .content .highlight .n{color:#adbac7}[data-theme='dark'] .single .content .highlight .na{color:#6cb6ff}[data-theme='dark'] .single .content .highlight .nb{color:#dcbdfb}[data-theme='dark'] .single .content .highlight .bp{color:#adbac7}[data-theme='dark'] .single .content .highlight .nc{color:#dcbdfb}[data-theme='dark'] .single .content .highlight .no{color:#6cb6ff}[data-theme='dark'] .single .content .highlight .nd{color:#dcbdfb}[data-theme='dark'] .single .content .highlight .ni{color:#6cb6ff}[data-theme='dark'] .single .content .highlight .ne{color:#6cb6ff}[data-theme='dark'] .single .content .highlight .nf{color:#dcbdfb}[data-theme='dark'] .single .content .highlight .fm{color:#6cb6ff}[data-theme='dark'] .single .content .highlight .nl{color:#6cb6ff}[data-theme='dark'] .single .content .highlight .nn{color:#f47067}[data-theme='dark'] .single .content .highlight .nx{color:#dcbdfb}[data-theme='dark'] .single .content .highlight .py{color:#6cb6ff}[data-theme='dark'] .single .content .highlight .nt{color:#8ddb8c}[data-theme='dark'] .single .content .highlight .nv{color:#f69d50}[data-theme='dark'] .single .content .highlight .vc{color:#6cb6ff}[data-theme='dark'] .single .content .highlight .vg{color:#6cb6ff}[data-theme='dark'] .single .content .highlight .vi{color:#6cb6ff}[data-theme='dark'] .single .content .highlight .vm{color:#6cb6ff}[data-theme='dark'] .single .content .highlight .l{color:#6cb6ff}[data-theme='dark'] .single .content .highlight .ld{color:#96d0ff}[data-theme='dark'] .single .content .highlight .s{color:#96d0ff}[data-theme='dark'] .single .content .highlight .sa{color:#96d0ff}[data-theme='dark'] .single .content .highlight .sb{color:#96d0ff}[data-theme='dark'] .single .content .highlight .sc{color:#96d0ff}[data-theme='dark'] .single .content .highlight .dl{color:#96d0ff}[data-theme='dark'] .single .content .highlight .sd{color:#96d0ff}[data-theme='dark'] .single .content .highlight .s2{color:#96d0ff}[data-theme='dark'] .single .content .highlight .se{color:#96d0ff}[data-theme='dark'] .single .content .highlight .sh{color:#96d0ff}[data-theme='dark'] .single .content .highlight .si{color:#96d0ff}[data-theme='dark'] .single .content .highlight .sx{color:#96d0ff}[data-theme='dark'] .single .content .highlight .sr{color:#96d0ff}[data-theme='dark'] .single .content .highlight .s1{color:#96d0ff}[data-theme='dark'] .single .content .highlight .ss{color:#96d0ff}[data-theme='dark'] .single .content .highlight .m{color:#6cb6ff}[data-theme='dark'] .single .content .highlight .mb{color:#6cb6ff}[data-theme='dark'] .single .content .highlight .mf{color:#6cb6ff}[data-theme='dark'] .single .content .highlight .mh{color:#6cb6ff}[data-theme='dark'] .single .content .highlight .mi{color:#6cb6ff}[data-theme='dark'] .single .content .highlight .il{color:#6cb6ff}[data-theme='dark'] .single .content .highlight .mo{color:#6cb6ff}[data-theme='dark'] .single .content .highlight .o{color:#f47067}[data-theme='dark'] .single .content .highlight .ow{color:#f47067}[data-theme='dark'] .single .content .highlight .p{color:#adbac7}[data-theme='dark'] .single .content .highlight .c{color:#768390}[data-theme='dark'] .single .content .highlight .ch{color:#768390}[data-theme='dark'] .single .content .highlight .cm{color:#768390}[data-theme='dark'] .single .content .highlight .c1{color:#768390}[data-theme='dark'] .single .content .highlight .cs{color:#768390}[data-theme='dark'] .single .content .highlight .cp{color:#6cb6ff}[data-theme='dark'] .single .content .highlight .cpf{color:#768390}[data-theme='dark'] .single .content .highlight .g{color:#adbac7}[data-theme='dark'] .single .content .highlight .gd{color:#ffd8d3}[data-theme='dark'] .single .content .highlight .ge{color:#adbac7}[data-theme='dark'] .single .content .highlight .gr{color:#dc3545}[data-theme='dark'] .single .content .highlight .gh{color:#adbac7}[data-theme='dark'] .single .content .highlight .gi{color:#b4f1b4}[data-theme='dark'] .single .content .highlight .go{color:#adbac7}[data-theme='dark'] .single .content .highlight .gp{color:#adbac7}[data-theme='dark'] .single .content .highlight .gs{color:#adbac7}[data-theme='dark'] .single .content .highlight .gu{color:#adbac7}[data-theme='dark'] .single .content .highlight .gt{color:#dc3545}[data-theme='dark'] .single .content .highlight .gl{color:#adbac7}[data-theme='dark'] .single .content .highlight .w{color:#adbac7}[data-theme='dark'] .single .content .highlight .x{color:#adbac7}.single .content .gist .gist-file,.single .content .gist .gist-data,.single .content .gist .gist-meta{border:none}.single .content .gist .gist-data{background-color:transparent}.single .content .gist .gist-meta{padding:0.4rem 0.8rem;background-color:#e6ebf1}.single .content .gist .gist-meta a,.single .content .gist .gist-meta a::before,.single .content .gist .gist-meta a::after{text-decoration:none;color:#2376b7}[data-theme='dark'] .single .content .gist .gist-meta a,[data-theme='dark'] .single .content .gist .gist-meta a::before,[data-theme='dark'] .single .content .gist .gist-meta a::after{color:#1781b5}.single .content .gist .gist-meta a:active,.single .content .gist .gist-meta a:hover{color:#ea517f}[data-theme='dark'] .single .content .gist .gist-meta a:active,[data-theme='dark'] .single .content .gist .gist-meta a:hover{color:#cc5595}[data-theme='dark'] .single .content .gist .gist-meta{background-color:#22262d}.single .content .gist .table-wrapper .highlight{background-color:#f6f8fa}[data-theme='dark'] .single .content .gist .table-wrapper .highlight{background-color:#2d333b}[data-theme='dark'] .single .content .gist{--fi-color-blob-num: #778491;--fi-color-blob-code-inner: #c5d1de;--fi-color-prettylights-syntax-comment: #768390;--fi-color-prettylights-syntax-constant: #6cb6ff;--fi-color-prettylights-syntax-constant-other-reference-link: #96d0ff;--fi-color-prettylights-syntax-entity: #dcbdfb;--fi-color-prettylights-syntax-storage-modifier-import: #adbac7;--fi-color-prettylights-syntax-entity-tag: #8ddb8c;--fi-color-prettylights-syntax-keyword: #f47067;--fi-color-prettylights-syntax-string: #96d0ff;--fi-color-prettylights-syntax-variable: #f69d50;--fi-color-prettylights-syntax-brackethighlighter-unmatched: #e5534b;--fi-color-prettylights-syntax-brackethighlighter-angle: #768390;--fi-color-prettylights-syntax-invalid-illegal-text: #cdd9e5;--fi-color-prettylights-syntax-invalid-illegal-bg: #922323;--fi-color-prettylights-syntax-carriage-return-text: #cdd9e5;--fi-color-prettylights-syntax-carriage-return-bg: #ad2e2c;--fi-color-prettylights-syntax-string-regexp: #8ddb8c;--fi-color-prettylights-syntax-markup-list: #eac55f;--fi-color-prettylights-syntax-markup-heading: #316dca;--fi-color-prettylights-syntax-markup-italic: #adbac7;--fi-color-prettylights-syntax-markup-bold: #adbac7;--fi-color-prettylights-syntax-markup-deleted-text: #ffd8d3;--fi-color-prettylights-syntax-markup-deleted-bg: #78191b;--fi-color-prettylights-syntax-markup-inserted-text: #b4f1b4;--fi-color-prettylights-syntax-markup-inserted-bg: #1b4721;--fi-color-prettylights-syntax-markup-changed-text: #ffddb0;--fi-color-prettylights-syntax-markup-changed-bg: #682d0f;--fi-color-prettylights-syntax-markup-ignored-text: #adbac7;--fi-color-prettylights-syntax-markup-ignored-bg: #255ab2;--fi-color-prettylights-syntax-meta-diff-range: #dcbdfb;--fi-color-prettylights-syntax-sublimelinter-gutter-mark: #545d68}[data-theme='dark'] .single .content .gist .blob-num{color:var(--fi-color-blob-num)}[data-theme='dark'] .single .content .gist .blob-code-inner{color:var(--fi-color-blob-code-inner)}[data-theme='dark'] .single .content .gist .pl-c{color:var(--fi-color-prettylights-syntax-comment)}[data-theme='dark'] .single .content .gist .pl-c1,[data-theme='dark'] .single .content .gist .pl-s .pl-v{color:var(--fi-color-prettylights-syntax-constant)}[data-theme='dark'] .single .content .gist .pl-e,[data-theme='dark'] .single .content .gist .pl-en{color:var(--fi-color-prettylights-syntax-entity)}[data-theme='dark'] .single .content .gist .pl-smi,[data-theme='dark'] .single .content .gist .pl-s .pl-s1{color:var(--fi-color-prettylights-syntax-storage-modifier-import)}[data-theme='dark'] .single .content .gist .pl-ent{color:var(--fi-color-prettylights-syntax-entity-tag)}[data-theme='dark'] .single .content .gist .pl-k{color:var(--fi-color-prettylights-syntax-keyword)}[data-theme='dark'] .single .content .gist .pl-s,[data-theme='dark'] .single .content .gist .pl-pds,[data-theme='dark'] .single .content .gist .pl-s .pl-pse .pl-s1,[data-theme='dark'] .single .content .gist .pl-sr,[data-theme='dark'] .single .content .gist .pl-sr .pl-cce,[data-theme='dark'] .single .content .gist .pl-sr .pl-sre,[data-theme='dark'] .single .content .gist .pl-sr .pl-sra{color:var(--fi-color-prettylights-syntax-string)}[data-theme='dark'] .single .content .gist .pl-v,[data-theme='dark'] .single .content .gist .pl-smw{color:var(--fi-color-prettylights-syntax-variable)}[data-theme='dark'] .single .content .gist .pl-bu{color:var(--fi-color-prettylights-syntax-brackethighlighter-unmatched)}[data-theme='dark'] .single .content .gist .pl-ii{color:var(--fi-color-prettylights-syntax-invalid-illegal-text);background-color:var(--fi-color-prettylights-syntax-invalid-illegal-bg)}[data-theme='dark'] .single .content .gist .pl-c2{color:var(--fi-color-prettylights-syntax-carriage-return-text);background-color:var(--fi-color-prettylights-syntax-carriage-return-bg)}[data-theme='dark'] .single .content .gist .pl-c2::before{content:'^M'}[data-theme='dark'] .single .content .gist .pl-sr .pl-cce{font-weight:bold;color:var(--fi-color-prettylights-syntax-string-regexp)}[data-theme='dark'] .single .content .gist .pl-ml{color:var(--fi-color-prettylights-syntax-markup-list)}[data-theme='dark'] .single .content .gist .pl-mh,[data-theme='dark'] .single .content .gist .pl-mh .pl-en,[data-theme='dark'] .single .content .gist .pl-ms{font-weight:bold;color:var(--fi-color-prettylights-syntax-markup-heading)}[data-theme='dark'] .single .content .gist .pl-mi{font-style:italic;color:var(--fi-color-prettylights-syntax-markup-italic)}[data-theme='dark'] .single .content .gist .pl-mb{font-weight:bold;color:var(--fi-color-prettylights-syntax-markup-bold)}[data-theme='dark'] .single .content .gist .pl-md{color:var(--fi-color-prettylights-syntax-markup-deleted-text);background-color:var(--fi-color-prettylights-syntax-markup-deleted-bg)}[data-theme='dark'] .single .content .gist .pl-mi1{color:var(--fi-color-prettylights-syntax-markup-inserted-text);background-color:var(--fi-color-prettylights-syntax-markup-inserted-bg)}[data-theme='dark'] .single .content .gist .pl-mc{color:var(--fi-color-prettylights-syntax-markup-changed-text);background-color:var(--fi-color-prettylights-syntax-markup-changed-bg)}[data-theme='dark'] .single .content .gist .pl-mi2{color:var(--fi-color-prettylights-syntax-markup-ignored-text);background-color:var(--fi-color-prettylights-syntax-markup-ignored-bg)}[data-theme='dark'] .single .content .gist .pl-mdr{font-weight:bold;color:var(--fi-color-prettylights-syntax-meta-diff-range)}[data-theme='dark'] .single .content .gist .pl-ba{color:var(--fi-color-prettylights-syntax-brackethighlighter-angle)}[data-theme='dark'] .single .content .gist .pl-sg{color:var(--fi-color-prettylights-syntax-sublimelinter-gutter-mark)}[data-theme='dark'] .single .content .gist .pl-corl{text-decoration:underline;color:var(--fi-color-prettylights-syntax-constant-other-reference-link)}.single .content .admonition{position:relative;margin:1rem 0;padding:0 0.75rem;border-left:.25rem solid;overflow:hidden;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;background-color:var(--fi-admonition-bg-color);border-left-color:var(--fi-admonition-color);--fi-admonition-color: #448aff;--fi-admonition-bg-color: rgba(68,138,255,0.1);--fi-admonition-bg-color-collapsed: rgba(68,138,255,0.25)}.single .content .admonition .admonition-title{font-weight:bold;margin:0 -0.75rem;padding:0.25rem 1.8rem;border-bottom:1px solid;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.single .content .admonition .admonition-content{padding:0.5rem 0}.single .content .admonition .admonition-content>p{margin:0}.single .content .admonition .admonition-content:empty{display:none}.single .content .admonition i.icon{font-size:0.85rem;position:absolute;top:0.6rem;left:0.4rem}.single .content .admonition i.details-icon{position:absolute;top:0.6rem;right:0.3rem}.single .content .admonition>.admonition-title{border-bottom-color:var(--fi-admonition-bg-color);background-color:var(--fi-admonition-bg-color-collapsed)}.single .content .admonition>.admonition-title i.icon{color:var(--fi-admonition-color)}.single .content .admonition.open>.admonition-title{background-color:var(--fi-admonition-bg-color)}.single .content .admonition.abstract{--fi-admonition-color: #00b0ff;--fi-admonition-bg-color: rgba(0,176,255,0.1);--fi-admonition-bg-color-collapsed: rgba(0,176,255,0.25)}.single .content .admonition.info{--fi-admonition-color: #00b8d4;--fi-admonition-bg-color: rgba(0,184,212,0.1);--fi-admonition-bg-color-collapsed: rgba(0,184,212,0.25)}.single .content .admonition.todo{--fi-admonition-color: #5e9aff;--fi-admonition-bg-color: rgba(94,154,255,0.1);--fi-admonition-bg-color-collapsed: rgba(94,154,255,0.25)}.single .content .admonition.tip{--fi-admonition-color: #00bfa5;--fi-admonition-bg-color: rgba(0,191,165,0.1);--fi-admonition-bg-color-collapsed: rgba(0,191,165,0.25)}.single .content .admonition.success{--fi-admonition-color: #00c853;--fi-admonition-bg-color: rgba(0,200,83,0.1);--fi-admonition-bg-color-collapsed: rgba(0,200,83,0.25)}.single .content .admonition.question{--fi-admonition-color: #ffbd67;--fi-admonition-bg-color: rgba(255,190,0,0.1);--fi-admonition-bg-color-collapsed: rgba(255,190,0,0.25)}.single .content .admonition.warning{--fi-admonition-color: #ff9100;--fi-admonition-bg-color: rgba(255,145,0,0.1);--fi-admonition-bg-color-collapsed: rgba(255,145,0,0.25)}.single .content .admonition.failure{--fi-admonition-color: #ff5252;--fi-admonition-bg-color: rgba(255,82,82,0.1);--fi-admonition-bg-color-collapsed: rgba(255,82,82,0.25)}.single .content .admonition.danger{--fi-admonition-color: #ff1744;--fi-admonition-bg-color: rgba(255,23,68,0.1);--fi-admonition-bg-color-collapsed: rgba(255,23,68,0.25)}.single .content .admonition.bug{--fi-admonition-color: #f50057;--fi-admonition-bg-color: rgba(245,0,87,0.1);--fi-admonition-bg-color-collapsed: rgba(245,0,87,0.25)}.single .content .admonition.example{--fi-admonition-color: #651fff;--fi-admonition-bg-color: rgba(101,31,255,0.1);--fi-admonition-bg-color-collapsed: rgba(101,31,255,0.25)}.single .content .admonition.quote{--fi-admonition-color: #9e9e9e;--fi-admonition-bg-color: rgba(159,159,159,0.1);--fi-admonition-bg-color-collapsed: rgba(159,159,159,0.25)}.single .content .admonition:last-child{margin-bottom:0.75rem}.single .content .bilibili{position:relative;width:100%;height:auto;aspect-ratio:16 / 9;margin:1rem auto;text-align:center}.single .content .bilibili iframe{position:absolute;width:100%;height:100%;left:0;top:0}.single .content .douyin{position:relative;width:100%;height:0;padding-bottom:60.725%;margin:1rem auto;text-align:center}.single .content .douyin iframe{position:absolute;width:100%;height:100%;left:0;top:0}@media only screen and (max-width: 680px){.single .content .douyin{width:92%;margin-inline:4%;padding-bottom:190%}}.single .content .card-link{position:relative;display:block;margin:0.5rem auto;box-sizing:border-box;width:clamp(50%, 400px, 100%);max-width:100%;overflow:hidden;text-decoration:none;border:none;-webkit-border-radius:.75rem;-moz-border-radius:.75rem;border-radius:.75rem}.single .content .cl-backdrop{position:absolute;top:0.75rem;bottom:0.75rem;left:0;right:0;background-image:var(--cl-bg-url);background-repeat:no-repeat;filter:blur(0.5rem);opacity:0.5;background-size:contain;background-position:center}.single .content .cl-content{position:relative;display:flex;align-items:center;justify-content:space-between;padding:0.75rem;background-color:rgba(245,245,245,0.88)}.single .content .cl-text{overflow:hidden}.single .content .cl-title{display:-webkit-box;-webkit-line-clamp:2;overflow:hidden;max-height:calc(1rem * 1.25 * 2);font-size:1rem;font-weight:500;line-height:1.25;color:#161209}.single .content .cl-meta{display:flex;align-items:center;margin-top:0.25rem;font-size:0.825rem;color:#b1b1ba}.single .content .cl-icon-link{width:1rem;height:1rem;flex-shrink:0;margin-right:0.25rem}.single .content .cl-url{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.single .content .cl-shortcut-icon{width:4rem;height:4rem;flex-shrink:0;margin-left:0.25rem}.single .content .cl-shortcut-icon:is(i){text-align:center;font-size:3rem;display:inline-flex;align-items:center;justify-content:center}.single .content .cl-shortcut-image{width:4rem;height:4rem !important;flex-shrink:0;margin-left:0.25rem;object-fit:cover;border-radius:50%}[data-theme='dark'] .single .content .cl-content{background-color:rgba(61,62,65,0.88)}[data-theme='dark'] .single .content .cl-title{color:#b1b1ba}[data-theme='dark'] .single .content .cl-meta{color:#909092}.single .content blockquote.blockquote-center{border-left:none;margin:40px 0;padding:0;position:relative;text-align:center;color:inherit}.single .content blockquote.blockquote-center::after,.single .content blockquote.blockquote-center::before{left:0;line-height:1;opacity:0.6;position:absolute;width:100%}.single .content blockquote.blockquote-center::before{border-top:1px solid #f0f0f0;text-align:left;top:-20px;content:'\f10d';font-family:'Font Awesome 5 Free';font-weight:900}[data-theme='dark'] .single .content blockquote.blockquote-center::before{border-top-color:#363636}.single .content blockquote.blockquote-center::after{border-bottom:1px solid #f0f0f0;bottom:-20px;text-align:right;content:'\f10e';font-family:'Font Awesome 5 Free';font-weight:900}[data-theme='dark'] .single .content blockquote.blockquote-center::after{border-bottom-color:#363636}.single .content .echarts{margin:0.5rem 0;text-align:center}[data-theme='dark'] .single .content iframe.instagram-media{border:none !important}.single .content .mapbox{margin:0.5rem 0;padding:0.5rem 0;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.single .content .mermaid[data-processed='true']{text-align:center}.single .content .mermaid svg{max-width:100%;height:auto}.single .content .fi-timeline{list-style:none;box-sizing:border-box;overflow:auto;margin:1rem auto;--timeline-color: #e3e3e3;--timeline-circle-color: #FF735A;--timeline-circle-size: 10px;--timeline-width: 2px}.single .content .fi-timeline[data-animation] .fi-timeline-item::before{border-color:var(--timeline-color);animation-name:border-color-fade;animation-duration:2s;animation-iteration-count:infinite;animation-delay:calc(var(--timeline-index) * 0.3s)}@keyframes border-color-fade{0%{border-color:var(--timeline-color)}50%{border-color:var(--timeline-circle-color)}100%{border-color:var(--timeline-color)}}.single .content .fi-timeline .fi-timeline-item{position:relative;padding:0.5rem 1.75rem}.single .content .fi-timeline .fi-timeline-item[data-type='primary']{--timeline-circle-color: var(--fi-primary)}.single .content .fi-timeline .fi-timeline-item[data-type='secondary']{--timeline-circle-color: var(--fi-secondary)}.single .content .fi-timeline .fi-timeline-item[data-type='success']{--timeline-circle-color: var(--fi-success)}.single .content .fi-timeline .fi-timeline-item[data-type='info']{--timeline-circle-color: var(--fi-info)}.single .content .fi-timeline .fi-timeline-item[data-type='warning']{--timeline-circle-color: var(--fi-warning)}.single .content .fi-timeline .fi-timeline-item[data-type='danger']{--timeline-circle-color: var(--fi-danger)}.single .content .fi-timeline .fi-timeline-item .fi-timeline-item__timestamp{font-size:0.8125rem;line-height:1;color:var(--fi-secondary)}.single .content .fi-timeline .fi-timeline-item .fi-timeline-item__timestamp.is-top{margin-bottom:0.25rem;padding-top:0.34375rem}.single .content .fi-timeline .fi-timeline-item .fi-timeline-item__timestamp.is-bottom{margin-top:0.25rem}.single .content .fi-timeline .fi-timeline-item .fi-timeline-item__content{font-size:0.875rem}.single .content .fi-timeline .fi-timeline-item[data-node='dot']::before{background-color:var(--timeline-circle-color) !important}.single .content .fi-timeline .fi-timeline-item[data-size='small']::before{transform:scale(0.8) translateY(-50%)}.single .content .fi-timeline .fi-timeline-item[data-size='large']::before{transform:scale(1.2) translateY(-50%)}.single .content .fi-timeline .fi-timeline-item::before{content:'';position:absolute;top:1.25rem;transform:translateY(-50%);left:0;width:var(--timeline-circle-size);height:var(--timeline-circle-size);font-size:var(--timeline-circle-size);line-height:var(--timeline-circle-size);text-align:center;border-radius:50%;border-width:var(--timeline-width);border-style:solid;border-color:var(--timeline-circle-color);background-color:#fff;color:transparent;z-index:1}[data-theme='dark'] .single .content .fi-timeline .fi-timeline-item::before{background-color:#292a2e}.single .content .fi-timeline .fi-timeline-item::after{content:'';position:absolute;top:0;left:calc(var(--timeline-circle-size) / 2 + var(--timeline-width) / 2);width:var(--timeline-width);height:100%;background-color:var(--timeline-color)}[data-theme='dark'] .single .content .fi-timeline .fi-timeline-item{--timeline-color: #717171}.single .post-reward{padding:1rem;text-align:center}.single .post-reward:has(.reward-ways:empty){display:none}.single .post-reward .comment{margin:0.5rem 0;font-weight:500}.single .post-reward .reward-ways{display:none}.single .post-reward .reward-input:checked~.reward-ways{display:block;padding-top:20px}.single .post-reward .reward-input:checked~.reward-ways div{display:inline-block;-webkit-user-select:none !important;-moz-user-select:none !important;user-select:none !important}.single .post-reward .reward-input:checked~.reward-ways div span{display:block}.single .post-reward .reward-input:checked~.reward-ways div:hover span[data-animation]{animation:var(--animation-reward, jackInTheBox) 0.8s infinite linear;pointer-events:none}.single .post-reward .reward-input:checked~.reward-ways div img{display:inline-block;margin:0.8em 2em 0;max-width:100%;width:180px}.single .post-reward .reward-input:checked~.reward-ways[data-mode='fixed']{position:fixed;top:50%;left:50%;transform:translate(-50%, -50%);padding:1rem;background-color:#fff;z-index:200;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;-webkit-box-shadow:0 0 1.5rem 0 rgba(0,0,0,0.2);box-shadow:0 0 1.5rem 0 rgba(0,0,0,0.2)}[data-theme='dark'] .single .post-reward .reward-input:checked~.reward-ways[data-mode='fixed']{background-color:#292a2e;-webkit-box-shadow:0 0 1.5rem 0 rgba(255,255,255,0.2);box-shadow:0 0 1.5rem 0 rgba(255,255,255,0.2)}.single .post-reward .reward-button{display:inline-block;box-sizing:border-box;cursor:pointer;outline:none;color:#ff7961;border:2px solid #ff8e7a;border-radius:5px;padding:0.6rem 1rem;background-color:#fff;vertical-align:text-top;line-height:1rem;-webkit-transition:all 0.3s ease-out;-moz-transition:all 0.3s ease-out;-o-transition:all 0.3s ease-out;transition:all 0.3s ease-out}.single .post-reward .reward-button:active,.single .post-reward .reward-button:hover{color:#ffffff;border-color:tomato;background-color:tomato}[data-theme='dark'] .single .post-reward .reward-button:active,[data-theme='dark'] .single .post-reward .reward-button:hover{color:#ffffff;border-color:#ff4d2e;background-color:#ff4d2e}[data-theme='dark'] .single .post-reward .reward-button{color:tomato;border-color:#ff7961;background-color:#292a2e}.single .post-footer{margin:2rem 0}.single .post-footer~.post-reward{margin-block:-2rem 0}.single .post-footer .post-info{border-bottom:1px solid #f0f0f0;padding:0.3rem 0}[data-theme='dark'] .single .post-footer .post-info{border-bottom:1px solid #363636}.single .post-footer .post-info .post-info-line{display:flex;justify-content:space-between}.single .post-footer .post-info .post-info-line .post-info-mod{font-size:0.8em;color:#b1b1ba}[data-theme='dark'] .single .post-footer .post-info .post-info-line .post-info-mod{color:#909092}.single .post-footer .post-info .post-info-line .post-info-mod a,.single .post-footer .post-info .post-info-line .post-info-mod a::before,.single .post-footer .post-info .post-info-line .post-info-mod a::after{text-decoration:none;color:#2376b7}[data-theme='dark'] .single .post-footer .post-info .post-info-line .post-info-mod a,[data-theme='dark'] .single .post-footer .post-info .post-info-line .post-info-mod a::before,[data-theme='dark'] .single .post-footer .post-info .post-info-line .post-info-mod a::after{color:#1781b5}.single .post-footer .post-info .post-info-line .post-info-mod a:active,.single .post-footer .post-info .post-info-line .post-info-mod a:hover{color:#ea517f}[data-theme='dark'] .single .post-footer .post-info .post-info-line .post-info-mod a:active,[data-theme='dark'] .single .post-footer .post-info .post-info-line .post-info-mod a:hover{color:#cc5595}.single .post-footer .post-info .post-info-line .post-info-license{font-size:0.8em;color:#b1b1ba}[data-theme='dark'] .single .post-footer .post-info .post-info-line .post-info-license{color:#909092}.single .post-footer .post-info .post-info-line .post-info-license a,.single .post-footer .post-info .post-info-line .post-info-license a::before,.single .post-footer .post-info .post-info-line .post-info-license a::after{text-decoration:none;color:#2376b7}[data-theme='dark'] .single .post-footer .post-info .post-info-line .post-info-license a,[data-theme='dark'] .single .post-footer .post-info .post-info-line .post-info-license a::before,[data-theme='dark'] .single .post-footer .post-info .post-info-line .post-info-license a::after{color:#1781b5}.single .post-footer .post-info .post-info-line .post-info-license a:active,.single .post-footer .post-info .post-info-line .post-info-license a:hover{color:#ea517f}[data-theme='dark'] .single .post-footer .post-info .post-info-line .post-info-license a:active,[data-theme='dark'] .single .post-footer .post-info .post-info-line .post-info-license a:hover{color:#cc5595}.single .post-footer .post-info .post-info-line .post-info-md{font-size:0.8rem;width:fit-content}.single .post-footer .post-info .post-info-line .post-info-md a,.single .post-footer .post-info .post-info-line .post-info-md a::before,.single .post-footer .post-info .post-info-line .post-info-md a::after{text-decoration:none;color:#2376b7}[data-theme='dark'] .single .post-footer .post-info .post-info-line .post-info-md a,[data-theme='dark'] .single .post-footer .post-info .post-info-line .post-info-md a::before,[data-theme='dark'] .single .post-footer .post-info .post-info-line .post-info-md a::after{color:#1781b5}.single .post-footer .post-info .post-info-line .post-info-md a:active,.single .post-footer .post-info .post-info-line .post-info-md a:hover{color:#ea517f}[data-theme='dark'] .single .post-footer .post-info .post-info-line .post-info-md a:active,[data-theme='dark'] .single .post-footer .post-info .post-info-line .post-info-md a:hover{color:#cc5595}.single .post-footer .post-info .post-info-line .post-info-md>span:not(:first-child)::before{content:'|';margin-left:0.25rem;margin-right:0.25rem}.single .post-footer .post-info .post-info-line .post-info-share a *{vertical-align:text-bottom}.single .post-footer .post-info-more{padding:0.3rem 0;display:flex;justify-content:space-between;font-size:0.9rem}.single .post-footer .post-tags{max-width:65%}.single .post-footer .post-tags *{display:inline}.single .post-footer .post-tags a,.single .post-footer .post-tags a::before,.single .post-footer .post-tags a::after{text-decoration:none;color:#161209}[data-theme='dark'] .single .post-footer .post-tags a,[data-theme='dark'] .single .post-footer .post-tags a::before,[data-theme='dark'] .single .post-footer .post-tags a::after{color:#b1b1ba}.single .post-footer .post-tags a:active,.single .post-footer .post-tags a:hover{color:#2983bb}[data-theme='dark'] .single .post-footer .post-tags a:active,[data-theme='dark'] .single .post-footer .post-tags a:hover{color:#fff}.single .post-footer .post-tags .post-tag:not(:last-child)::after{content:',';margin-right:0.25rem}.single .post-footer .post-nav{display:flex;justify-content:space-between;gap:1.5rem;padding-block:0.5rem}.single .post-footer .post-nav .post-nav-item{flex:1;font-size:1rem;font-weight:600;-webkit-transition:all 0.3s ease-out;-moz-transition:all 0.3s ease-out;-o-transition:all 0.3s ease-out;transition:all 0.3s ease-out}.single .post-footer .post-nav .post-nav-item[rel='next']{text-align:right}.single .post-footer .post-nav .post-nav-item[rel='prev']:hover{-webkit-transform:translateX(-4px);-ms-transform:translateX(-4px);-o-transform:translateX(-4px);transform:translateX(-4px)}.single .post-footer .post-nav .post-nav-item[rel='next']:hover{-webkit-transform:translateX(4px);-ms-transform:translateX(4px);-o-transform:translateX(4px);transform:translateX(4px)}.single>#comments{padding:2rem 0}.single>#comments iframe{width:100%}.lg-toolbar .lg-icon::after{color:#999}.special .single-title,.special .single-subtitle{text-align:right}.special.friends .friend-links{margin-top:1rem;display:flex;flex-direction:row;justify-content:space-between;flex-wrap:wrap}@media (max-width: 576px){.special.friends .friend-links{justify-content:space-around}}.special.friends .friend-link{width:150px;height:200px;font-size:1rem;text-align:center;background:rgba(255,255,255,0.3);box-sizing:border-box;box-shadow:3px 3px 5px #aaa;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;border:none;transition-duration:0.3s;margin-bottom:1rem;display:flex;flex-direction:column;justify-content:space-between}.special.friends .friend-link:hover{background:#fff;-webkit-transform:scale(1.03);-ms-transform:scale(1.03);-o-transform:scale(1.03);transform:scale(1.03);box-shadow:0 0 3px #aaa}.special.friends .friend-avatar{object-fit:cover;object-position:center;width:100% !important;height:150px !important;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;margin:0;padding:0}.special.friends .friend-nickname{display:block;position:relative;color:#2bbc8a;font-weight:bold;max-width:100%;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;line-height:18px;margin-bottom:1rem}.special.friends .friend-nickname:hover{color:#d480aa}.special.search .gcse-searchbox:empty,.special.search .gcse-searchresults:empty{display:none}.archive .single-title{padding:1rem 0 0.5rem;margin:0;font-size:1.6rem;font-weight:bold;line-height:140%;text-align:right}.archive .single-subtitle{margin:0.5rem 0;font-size:1.2rem;font-weight:normal;line-height:100%;text-align:right}.archive .group-title{font-size:1.2rem;margin-top:1.5rem;margin-bottom:1rem}.archive .taxonomy-cards{margin:0 auto;display:flex;align-items:flex-start;justify-content:space-between;flex-direction:row;flex-wrap:wrap;line-height:1.6rem}.archive .taxonomy-cards .card-item{font-size:0.875rem;text-align:left;width:45%;display:flex;align-items:flex-start;min-height:10rem;padding:0 2%;position:relative}.archive .taxonomy-cards .card-item:nth-child(n+3){margin-top:2rem}.archive .taxonomy-cards .card-item .card-item-wrapper{width:100%;overflow:hidden}.archive .taxonomy-cards .card-item .card-item-wrapper .card-item-title{font-size:1.2rem;font-weight:bold;display:inline-block;margin-top:1rem;margin-bottom:0.75rem}.archive .taxonomy-cards .card-item .card-item-wrapper span{float:right;padding-right:1rem}.archive .archive-item{display:flex;justify-content:space-between;align-items:center;box-sizing:border-box;margin:0.25rem 0 0.25rem 1.5rem}.archive .archive-item-link{min-width:10%;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.archive .archive-item-link:hover{color:#2983bb;background-color:transparent}[data-theme='dark'] .archive .archive-item-link{color:#b1b1ba}[data-theme='dark'] .archive .archive-item-link:hover{color:#fff}.archive .archive-item-date{text-align:right;color:#b1b1ba;white-space:nowrap;font-variant-numeric:tabular-nums lining-nums}[data-theme='dark'] .archive .archive-item-date{color:#909092}.archive .more-post{text-align:right}.archive .tag-cloud-tags{margin:10px 0}.archive .tag-cloud-tags a,.archive .tag-cloud-tags a::before,.archive .tag-cloud-tags a::after{text-decoration:none;color:#161209}[data-theme='dark'] .archive .tag-cloud-tags a,[data-theme='dark'] .archive .tag-cloud-tags a::before,[data-theme='dark'] .archive .tag-cloud-tags a::after{color:#b1b1ba}.archive .tag-cloud-tags a:active,.archive .tag-cloud-tags a:hover{color:#2983bb}[data-theme='dark'] .archive .tag-cloud-tags a:active,[data-theme='dark'] .archive .tag-cloud-tags a:hover{color:#fff}.archive .tag-cloud-tags a{display:inline-block;position:relative;margin:5px 10px;word-wrap:break-word;overflow-wrap:break-word;-webkit-transition:all ease-out 0.3s;-moz-transition:all ease-out 0.3s;-o-transition:all ease-out 0.3s;transition:all ease-out 0.3s}.archive .tag-cloud-tags a:active,.archive .tag-cloud-tags a:focus,.archive .tag-cloud-tags a:hover{-webkit-transform:scale(1.2);-ms-transform:scale(1.2);-o-transform:scale(1.2);transform:scale(1.2)}.archive .tag-cloud-tags a:active sup,.archive .tag-cloud-tags a:focus sup,.archive .tag-cloud-tags a:hover sup{color:#161209;-webkit-transition:all ease-out 0.3s;-moz-transition:all ease-out 0.3s;-o-transition:all ease-out 0.3s;transition:all ease-out 0.3s}[data-theme='dark'] .archive .tag-cloud-tags a:active sup,[data-theme='dark'] .archive .tag-cloud-tags a:focus sup,[data-theme='dark'] .archive .tag-cloud-tags a:hover sup{color:#b1b1ba}.archive .tag-cloud-tags a sup{color:#b1b1ba}[data-theme='dark'] .archive .tag-cloud-tags a sup{color:#909092}.archive .tag-cloud-0{color:#b1b1ba !important;font-size:var(--tag-font);margin:10px !important}.archive .tag-cloud-0:active,.archive .tag-cloud-0:focus,.archive .tag-cloud-0:hover{color:#2983bb !important}[data-theme='dark'] .archive .tag-cloud-0:active,[data-theme='dark'] .archive .tag-cloud-0:focus,[data-theme='dark'] .archive .tag-cloud-0:hover{color:#fff}.archive .tag-cloud-0:active sup,.archive .tag-cloud-0:focus sup,.archive .tag-cloud-0:hover sup{opacity:1}[data-theme='dark'] .archive .tag-cloud-0{color:#909092 !important}.archive .tag-cloud-0 sup{opacity:0}.archive .tag-cloud-1{color:#a2a1a8 !important;font-size:var(--tag-font);margin:10px !important}.archive .tag-cloud-1:active,.archive .tag-cloud-1:focus,.archive .tag-cloud-1:hover{color:#2983bb !important}[data-theme='dark'] .archive .tag-cloud-1:active,[data-theme='dark'] .archive .tag-cloud-1:focus,[data-theme='dark'] .archive .tag-cloud-1:hover{color:#fff}.archive .tag-cloud-1:active sup,.archive .tag-cloud-1:focus sup,.archive .tag-cloud-1:hover sup{opacity:1}[data-theme='dark'] .archive .tag-cloud-1{color:#939396 !important}.archive .tag-cloud-1 sup{opacity:0}.archive .tag-cloud-2{color:#929197 !important;font-size:var(--tag-font);margin:10px !important}.archive .tag-cloud-2:active,.archive .tag-cloud-2:focus,.archive .tag-cloud-2:hover{color:#2983bb !important}[data-theme='dark'] .archive .tag-cloud-2:active,[data-theme='dark'] .archive .tag-cloud-2:focus,[data-theme='dark'] .archive .tag-cloud-2:hover{color:#fff}.archive .tag-cloud-2:active sup,.archive .tag-cloud-2:focus sup,.archive .tag-cloud-2:hover sup{opacity:1}[data-theme='dark'] .archive .tag-cloud-2{color:#97979a !important}.archive .tag-cloud-2 sup{opacity:0}.archive .tag-cloud-3{color:#838185 !important;font-size:var(--tag-font);margin:10px !important}.archive .tag-cloud-3:active,.archive .tag-cloud-3:focus,.archive .tag-cloud-3:hover{color:#2983bb !important}[data-theme='dark'] .archive .tag-cloud-3:active,[data-theme='dark'] .archive .tag-cloud-3:focus,[data-theme='dark'] .archive .tag-cloud-3:hover{color:#fff}.archive .tag-cloud-3:active sup,.archive .tag-cloud-3:focus sup,.archive .tag-cloud-3:hover sup{opacity:1}[data-theme='dark'] .archive .tag-cloud-3{color:#9a9a9e !important}.archive .tag-cloud-3 sup{opacity:0}.archive .tag-cloud-4{color:#737173 !important;font-size:var(--tag-font);margin:10px !important}.archive .tag-cloud-4:active,.archive .tag-cloud-4:focus,.archive .tag-cloud-4:hover{color:#2983bb !important}[data-theme='dark'] .archive .tag-cloud-4:active,[data-theme='dark'] .archive .tag-cloud-4:focus,[data-theme='dark'] .archive .tag-cloud-4:hover{color:#fff}.archive .tag-cloud-4:active sup,.archive .tag-cloud-4:focus sup,.archive .tag-cloud-4:hover sup{opacity:1}[data-theme='dark'] .archive .tag-cloud-4{color:#9d9da2 !important}.archive .tag-cloud-4 sup{opacity:0}.archive .tag-cloud-5{color:#646262 !important;font-size:var(--tag-font);margin:10px !important}.archive .tag-cloud-5:active,.archive .tag-cloud-5:focus,.archive .tag-cloud-5:hover{color:#2983bb !important}[data-theme='dark'] .archive .tag-cloud-5:active,[data-theme='dark'] .archive .tag-cloud-5:focus,[data-theme='dark'] .archive .tag-cloud-5:hover{color:#fff}.archive .tag-cloud-5:active sup,.archive .tag-cloud-5:focus sup,.archive .tag-cloud-5:hover sup{opacity:1}[data-theme='dark'] .archive .tag-cloud-5{color:#a1a1a6 !important}.archive .tag-cloud-5 sup{opacity:0}.archive .tag-cloud-6{color:#545250 !important;font-size:var(--tag-font);margin:10px !important}.archive .tag-cloud-6:active,.archive .tag-cloud-6:focus,.archive .tag-cloud-6:hover{color:#2983bb !important}[data-theme='dark'] .archive .tag-cloud-6:active,[data-theme='dark'] .archive .tag-cloud-6:focus,[data-theme='dark'] .archive .tag-cloud-6:hover{color:#fff}.archive .tag-cloud-6:active sup,.archive .tag-cloud-6:focus sup,.archive .tag-cloud-6:hover sup{opacity:1}[data-theme='dark'] .archive .tag-cloud-6{color:#a4a4aa !important}.archive .tag-cloud-6 sup{opacity:0}.archive .tag-cloud-7{color:#45423e !important;font-size:var(--tag-font);margin:10px !important}.archive .tag-cloud-7:active,.archive .tag-cloud-7:focus,.archive .tag-cloud-7:hover{color:#2983bb !important}[data-theme='dark'] .archive .tag-cloud-7:active,[data-theme='dark'] .archive .tag-cloud-7:focus,[data-theme='dark'] .archive .tag-cloud-7:hover{color:#fff}.archive .tag-cloud-7:active sup,.archive .tag-cloud-7:focus sup,.archive .tag-cloud-7:hover sup{opacity:1}[data-theme='dark'] .archive .tag-cloud-7{color:#a7a7ae !important}.archive .tag-cloud-7 sup{opacity:0}.archive .tag-cloud-8{color:#35322c !important;font-size:var(--tag-font);margin:10px !important}.archive .tag-cloud-8:active,.archive .tag-cloud-8:focus,.archive .tag-cloud-8:hover{color:#2983bb !important}[data-theme='dark'] .archive .tag-cloud-8:active,[data-theme='dark'] .archive .tag-cloud-8:focus,[data-theme='dark'] .archive .tag-cloud-8:hover{color:#fff}.archive .tag-cloud-8:active sup,.archive .tag-cloud-8:focus sup,.archive .tag-cloud-8:hover sup{opacity:1}[data-theme='dark'] .archive .tag-cloud-8{color:#aaaab2 !important}.archive .tag-cloud-8 sup{opacity:0}.archive .tag-cloud-9{color:#26221b !important;font-size:var(--tag-font);margin:10px !important}.archive .tag-cloud-9:active,.archive .tag-cloud-9:focus,.archive .tag-cloud-9:hover{color:#2983bb !important}[data-theme='dark'] .archive .tag-cloud-9:active,[data-theme='dark'] .archive .tag-cloud-9:focus,[data-theme='dark'] .archive .tag-cloud-9:hover{color:#fff}.archive .tag-cloud-9:active sup,.archive .tag-cloud-9:focus sup,.archive .tag-cloud-9:hover sup{opacity:1}[data-theme='dark'] .archive .tag-cloud-9{color:#aeaeb6 !important}.archive .tag-cloud-9 sup{opacity:0}.archive .tag-cloud-10{color:#161209 !important;font-size:var(--tag-font);margin:10px !important}.archive .tag-cloud-10:active,.archive .tag-cloud-10:focus,.archive .tag-cloud-10:hover{color:#2983bb !important}[data-theme='dark'] .archive .tag-cloud-10:active,[data-theme='dark'] .archive .tag-cloud-10:focus,[data-theme='dark'] .archive .tag-cloud-10:hover{color:#fff}.archive .tag-cloud-10:active sup,.archive .tag-cloud-10:focus sup,.archive .tag-cloud-10:hover sup{opacity:1}[data-theme='dark'] .archive .tag-cloud-10{color:#b1b1ba !important}.archive .tag-cloud-10 sup{opacity:0}.home .home-profile{text-align:center}.home .home-profile .home-avatar{padding:0.5rem}.home .home-profile .home-avatar img{display:inline-block;width:8rem;height:auto;margin:0 auto;-webkit-border-radius:100%;-moz-border-radius:100%;border-radius:100%;-webkit-box-shadow:0 0 0 0.3618em rgba(0,0,0,0.05);box-shadow:0 0 0 0.3618em rgba(0,0,0,0.05);-webkit-transition:all 0.4s ease;-moz-transition:all 0.4s ease;-o-transition:all 0.4s ease;transition:all 0.4s ease}[data-theme='dark'] .home .home-profile .home-avatar img{-webkit-box-shadow:0 0 0 0.3618em rgba(255,255,255,0.05);box-shadow:0 0 0 0.3618em rgba(255,255,255,0.05)}.home .home-profile .home-avatar img:hover{position:relative;-webkit-transform:translateY(-0.75rem);-ms-transform:translateY(-0.75rem);-o-transform:translateY(-0.75rem);transform:translateY(-0.75rem)}.home .home-profile .home-title{font-size:1.25rem;font-weight:bold;margin:0}.home .home-profile .home-title:not(:empty){padding:0.5rem}.home .home-profile .home-subtitle{font-size:1rem;font-weight:normal;margin:0;padding:0.5rem}.home .home-profile .links{padding:0.5rem;font-size:1.5rem}.home .home-profile .links a *{vertical-align:text-bottom}.home .home-profile .links img{height:1.5rem;padding:0 0.25rem}.home .home-profile .home-disclaimer{font-size:1rem;line-height:1.5rem;font-weight:normal;margin:0;padding:0.5rem;color:#b1b1ba}[data-theme='dark'] .home .home-profile .home-disclaimer{color:#909092}.home.posts .home-profile{padding:2rem 0}.home.posts .home-avatar img{width:6rem}.home.posts .summary{padding-block:1rem;color:var(--fi-global-font-color)}.home.posts .summary:not(:last-of-type)::after{content:'';position:absolute;width:100%;padding:2px;transform:translateY(18px);background:repeating-linear-gradient(135deg, var(--fi-hr-background-color) 0px, var(--fi-hr-background-color) 4px, transparent 4px, transparent 8px)}.home.posts .summary:first-of-type{padding-top:0}.home.posts .summary .featured-image-preview{width:100%;aspect-ratio:auto 1000 / 420;position:relative;margin:0.6rem auto;-webkit-transition:transform 0.4s ease;-moz-transition:transform 0.4s ease;-o-transition:transform 0.4s ease;transition:transform 0.4s ease}.home.posts .summary .featured-image-preview img{position:absolute;width:100%;height:100%;left:0;top:0;-o-object-fit:cover;object-fit:cover;font-family:"object-fit: cover;"}.home.posts .summary .featured-image-preview:hover{-webkit-transform:scale(1.01);-ms-transform:scale(1.01);-o-transform:scale(1.01);transform:scale(1.01)}.home.posts .summary .single-title{font-size:1.25rem;line-height:140%;margin:0.4rem 0}.home.posts .summary .content{margin-top:0.3rem;color:#b1b1ba}[data-theme='dark'] .home.posts .summary .content{color:#909092}.home.posts .summary .content a,.home.posts .summary .content a::before,.home.posts .summary .content a::after{text-decoration:none;color:#2376b7}[data-theme='dark'] .home.posts .summary .content a,[data-theme='dark'] .home.posts .summary .content a::before,[data-theme='dark'] .home.posts .summary .content a::after{color:#b1b1ba}.home.posts .summary .content a:active,.home.posts .summary .content a:hover{color:#ea517f}[data-theme='dark'] .home.posts .summary .content a:active,[data-theme='dark'] .home.posts .summary .content a:hover{color:#fff}.home.posts .summary .content b,.home.posts .summary .content strong{color:#b1b1ba}[data-theme='dark'] .home.posts .summary .content b,[data-theme='dark'] .home.posts .summary .content strong{color:#909092}.home.posts .summary .post-footer{margin-block:0.4rem 0;display:flex;justify-content:space-between;align-items:center;font-size:0.875rem}.home.posts .summary .post-footer a,.home.posts .summary .post-footer a::before,.home.posts .summary .post-footer a::after{text-decoration:none;color:#2376b7}[data-theme='dark'] .home.posts .summary .post-footer a,[data-theme='dark'] .home.posts .summary .post-footer a::before,[data-theme='dark'] .home.posts .summary .post-footer a::after{color:#1781b5}.home.posts .summary .post-footer a:active,.home.posts .summary .post-footer a:hover{color:#ea517f}[data-theme='dark'] .home.posts .summary .post-footer a:active,[data-theme='dark'] .home.posts .summary .post-footer a:hover{color:#cc5595}.home.posts .summary .post-footer .post-tags{padding:0}.home.posts .summary .post-footer .post-tags a,.home.posts .summary .post-footer .post-tags a::before,.home.posts .summary .post-footer .post-tags a::after{text-decoration:none;color:#161209}[data-theme='dark'] .home.posts .summary .post-footer .post-tags a,[data-theme='dark'] .home.posts .summary .post-footer .post-tags a::before,[data-theme='dark'] .home.posts .summary .post-footer .post-tags a::after{color:#b1b1ba}.home.posts .summary .post-footer .post-tags a:active,.home.posts .summary .post-footer .post-tags a:hover{color:#2983bb}[data-theme='dark'] .home.posts .summary .post-footer .post-tags a:active,[data-theme='dark'] .home.posts .summary .post-footer .post-tags a:hover{color:#fff}.home.posts .summary .post-footer .post-tags .post-tag:not(:last-child)::after{content:',';margin-right:0.25rem}#content-404{font-size:1.8rem;line-height:3rem;-webkit-transform:translateY(30vh);-ms-transform:translateY(30vh);-o-transform:translateY(30vh);transform:translateY(30vh);text-align:center}#content-offline{-webkit-transform:translateY(30vh);-ms-transform:translateY(30vh);-o-transform:translateY(30vh);transform:translateY(30vh);text-align:center}#content-offline .offline-title{font-size:3.6rem}#content-offline .offline-subtitle{margin:0.4rem 0;font-size:1rem;font-weight:normal;font-style:italic;line-height:100%;color:#57606a}#content-offline .error-text{font-size:1.2rem}@media only screen and (min-width: 1441px){.breadcrumb-container .breadcrumb,.page{width:ROUND(60%, 2px)}[data-page-style='wide'] .breadcrumb-container .breadcrumb,.breadcrumb-container [data-page-style='wide'] .breadcrumb,[data-page-style='wide'] .page{width:ROUND(64%, 2px)}[data-page-style='narrow'] .breadcrumb-container .breadcrumb,.breadcrumb-container [data-page-style='narrow'] .breadcrumb,[data-page-style='narrow'] .page{max-width:800px}}@media only screen and (max-width: 1440px){.breadcrumb-container .breadcrumb,.page{width:ROUND(56%, 2px)}[data-page-style='wide'] .breadcrumb-container .breadcrumb,.breadcrumb-container [data-page-style='wide'] .breadcrumb,[data-page-style='wide'] .page{width:ROUND(60%, 2px)}}@media only screen and (max-width: 1200px){.breadcrumb-container .breadcrumb,.page{width:ROUND(52%, 2px)}[data-page-style='wide'] .breadcrumb-container .breadcrumb,.breadcrumb-container [data-page-style='wide'] .breadcrumb,[data-page-style='wide'] .page{width:ROUND(56%, 2px)}#header-desktop .header-wrapper{padding-right:1rem}.search-dropdown.desktop{right:1rem}}@media only screen and (max-width: 960px){.breadcrumb-container .breadcrumb,.page{width:ROUND(80%, 2px) !important}aside{display:none !important}#toc-static{display:block}#header-desktop .header-wrapper{padding-left:1rem}}@media only screen and (min-width: 681px){.d-none-desktop{display:none}body:not([data-header-desktop='sticky']) .breadcrumb-container.sticky{top:0}body:not([data-header-desktop='auto']) .breadcrumb-container.sticky+.container .content [id]{scroll-margin-top:calc(var(--fi-scroll-mt) + var(--fi-breadcrumb-height))}[data-header-desktop='normal'] .page .content [id]{--fi-scroll-mt: .5rem}}@media only screen and (max-width: 680px){#header-desktop{display:none}#header-mobile{display:block}.d-none-mobile{display:none !important}body:not([data-header-mobile='sticky']) .breadcrumb-container.sticky{top:0}body:not([data-header-mobile='auto']) .breadcrumb-container.sticky+.container .content [id]{scroll-margin-top:calc(var(--fi-scroll-mt) + var(--fi-breadcrumb-height))}[data-header-mobile='normal'] .page .content [id]{--fi-scroll-mt: .5rem}.breadcrumb-container .breadcrumb,.page{width:100% !important}.page .taxonomy-cards .card-item{width:100%}footer .footer-container{font-size:0.618rem}.pagination .page-item{margin:0 5px}}@media only print{:root{--fi-scrollbar-width: none !important}*::-webkit-scrollbar{display:none !important;width:0 !important;height:0 !important}.scrollbar-thin{scrollbar-width:none !important;-ms-overflow-style:none !important}@page{size:A4 portrait;margin:1.27cm}.page{width:100% !important;padding-top:0 !important}.page.single .single-title,.page.single .single-subtitle,.page.single .post-meta{text-align:center}.page h1:not(.single-title),.page .page-break-before{page-break-before:always}.page .page-break-after{page-break-after:always}.print-d-none,.fixed-button,.github-corner,#postChat_button,.post-TianliGPT,.reading-progress-bar,header,.breadcrumb-container,footer,#toc-auto,.collection-card,.aside-collection,.single .post-meta .comment-visitors,.single .post-meta .comment-count,.single .content .highlight>.chroma .code-header .ellipses,.single .content .highlight>.chroma .code-header .edit,.single .content .highlight>.chroma .code-header .copy,.single .post-reward,.single .post-footer .post-info .post-info-line .post-info-share,.single .post-footer .post-info-more section:last-child,.single .post-footer .post-nav,.single>#comments{display:none !important}}@keyframes fi-pulse{from{-webkit-transform:scale3d(1, 1, 1);-ms-transform:scale3d(1, 1, 1);-o-transform:scale3d(1, 1, 1);transform:scale3d(1, 1, 1)}50%{-webkit-transform:scale3d(1.05, 1.05, 1.05);-ms-transform:scale3d(1.05, 1.05, 1.05);-o-transform:scale3d(1.05, 1.05, 1.05);transform:scale3d(1.05, 1.05, 1.05)}to{-webkit-transform:scale3d(1, 1, 1);-ms-transform:scale3d(1, 1, 1);-o-transform:scale3d(1, 1, 1);transform:scale3d(1, 1, 1)}}.animate__pulse{-webkit-animation-name:fi-pulse !important;animation-name:fi-pulse !important}@supports not (width: ROUND(60%, 2px)){@media only screen and (min-width: 1441px){.breadcrumb-container .breadcrumb,.page{width:60%}[data-page-style='wide'] .breadcrumb-container .breadcrumb,.breadcrumb-container [data-page-style='wide'] .breadcrumb,[data-page-style='wide'] .page{width:64%}[data-page-style='narrow'] .breadcrumb-container .breadcrumb,.breadcrumb-container [data-page-style='narrow'] .breadcrumb,[data-page-style='narrow'] .page{max-width:800px}}@media only screen and (max-width: 1440px){.breadcrumb-container .breadcrumb,.page{width:56%}[data-page-style='wide'] .breadcrumb-container .breadcrumb,.breadcrumb-container [data-page-style='wide'] .breadcrumb,[data-page-style='wide'] .page{width:60%}}@media only screen and (max-width: 1200px){.breadcrumb-container .breadcrumb,.page{width:52%}[data-page-style='wide'] .breadcrumb-container .breadcrumb,.breadcrumb-container [data-page-style='wide'] .breadcrumb,[data-page-style='wide'] .page{width:56%}}@media only screen and (max-width: 960px){.breadcrumb-container .breadcrumb,.page{width:80% !important}}@media only screen and (max-width: 680px){.breadcrumb-container .breadcrumb,.page{width:100% !important}}}
+
+/*# sourceMappingURL=style.min.css.map */
\ No newline at end of file
diff --git a/resources/_gen/assets/css/style.scss_1a67ae4ed98f18e3ea7da02d2ccd80c9.json b/resources/_gen/assets/css/style.scss_1a67ae4ed98f18e3ea7da02d2ccd80c9.json
new file mode 100644
index 00000000..0095485d
--- /dev/null
+++ b/resources/_gen/assets/css/style.scss_1a67ae4ed98f18e3ea7da02d2ccd80c9.json
@@ -0,0 +1 @@
+{"Target":"css/style.min.css","MediaType":"text/css","Data":{}}
\ No newline at end of file
diff --git a/scaffolds/draft.md b/scaffolds/draft.md
deleted file mode 100644
index 498e95ba..00000000
--- a/scaffolds/draft.md
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: {{ title }}
-tags:
----
diff --git a/scaffolds/page.md b/scaffolds/page.md
deleted file mode 100644
index f01ba3cd..00000000
--- a/scaffolds/page.md
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: {{ title }}
-date: {{ date }}
----
diff --git a/scaffolds/post.md b/scaffolds/post.md
deleted file mode 100644
index 63a98a58..00000000
--- a/scaffolds/post.md
+++ /dev/null
@@ -1,16 +0,0 @@
----
-title: {{ title }}
-date: {{ date }}
-tags:
-categories:
-comments: true
-fancybox: false
-description:
-toc: true
-permalink: {{ title }}
-top:
----
-## 前言
-
-
-
diff --git a/source/404/index.md b/source/404/index.md
deleted file mode 100644
index f4cbaec9..00000000
--- a/source/404/index.md
+++ /dev/null
@@ -1,71 +0,0 @@
----
-title: 404 Not Found:该页无法显示
-toc: false
-comments: false
-permalink: /404
----
-
-很抱歉,您所访问的地址并不存在
-回主页 · 所有文章 · 留言板
- 以下是博主喜欢的一些歌曲,可以听听,稍作休息~
-
-
-
-
-
diff --git a/source/_data/styles.styl b/source/_data/styles.styl
deleted file mode 100644
index b3b276eb..00000000
--- a/source/_data/styles.styl
+++ /dev/null
@@ -1,600 +0,0 @@
-// Custom styles.
-html,
-body,
- //span,
-p,
-h1,
-h2,
-h3,
-h4,
-h5,
-h6 {
- //font-family: lato,pingfang sc,microsoft yahei,sans-serif;
- color: #2e405b;
-}
-
-p {
- margin: 20px 0;
-}
-
-body {
- //background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAACFJREFUeNpiZGBg4GegAsBlCD8TqSYNQg2Mo6FEBAAIMACdPABtrSW/IQAAAABJRU5ErkJggg==");
- //background-repeat: repeat;
- //background-attachment: fixed;
- -webkit-font-smoothing: antialiased;
- text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.04);
- min-height: 100vh;
- margin: 0;
- background-image: linear-gradient(90deg, rgba(50, 0, 0, 0.03) 3%, rgba(0, 0, 0, 0) 3%), linear-gradient(360deg, rgba(50, 0, 0, 0.03) 3%, rgba(0, 0, 0, 0) 3%);
- background-size: 20px 20px;
- background-position: center center;
-}
-
-.sidebar-inner, .comments, .post-block, .sidebar, .header-inner {
- background: unset;
- box-shadow: unset;
-}
-
-.header-inner {
- margin: 0 auto;
- padding: 20px 0 70px;
-}
-
-// 修改网站头部颜色
-.headband {
- height: 3px;
- background: transparent;
-}
-
-.site-meta {
- padding: 20px 0;
- color: #2e405b;
- background: transparent;
-}
-
-// 站点名背景
-.brand {
- background-color: rgba(0, 0, 0, 0);
- margin-top: 15px;
- padding: 0px;
-}
-
-// 站点名字体
-.site-title {
- font-size: 32px;
- color: #2e405b;
- line-height: 80px;
- letter-spacing: 3px;
-}
-
-// 站点子标题
-.site-subtitle {
- margin: 0px;
- font-size: 26px;
- letter-spacing: 1px;
- padding-bottom: 3px;
- color: #2e405b;
-}
-
-// 菜单
-.menu {
- text-align: center;
- margin-top: 0px;
- margin-bottom: 0px;
- padding: 5px;
- box-shadow: 1px 2px 2px 2px #2e405b;
-}
-
-.menu:hover {
- text-align: center;
- margin-top: 0px;
- margin-bottom: 0px;
- padding: 5px;
- box-shadow: 2px 4px 4px 4px #2e405b;
-}
-
-// 菜单超链接字体大小
-.menu .menu-item a {
- font-size: 14px;
-}
-
-// 菜单各项边距
-.menu .menu-item {
- margin: 5px 15px;
-}
-
-// 菜单超链接样式
-.menu .menu-item a:hover {
- border-bottom-color: rgba(161, 102, 171, 0);
-}
-
-.menu a {
- background-color: rgba(0, 0, 0, 0);
- color: #2e405b;
-}
-
-// 修改按键(button)样式
-.btn {
- color: #49b1f5;
- background: #fff;
- border: 2px solid #49b1f5;
-}
-
-// 按键(button)点击时样式
-.btn:hover {
- border-color: #49b1f5;
- color: #fff;
- background: #49b1f5;
-}
-
-
-// 修改选中字符的颜色
-/* webkit, opera, IE9 */
-::selection {
- background: #00c4b6;
- color: #f7f7f7;
-}
-
-/* firefox */
-::-moz-selection {
- background: #00c4b6;
- color: #f7f7f7;
-}
-
-// 删除线
-del {
- color: #b35888;
- background: #fbf7f8;
- margin: 2px;
-}
-
-//页脚
-.footer-inner {
- color: #ff7600;
-}
-
-/* 去掉图片边框 */
-.posts-expand .content img {
- border: none;
- padding: 0px;
-}
-
-.post-gallery .post-gallery-img img {
- padding: 3px;
-}
-
-// Custom styles
-// 页面最顶部的横线
-.headband {
- height: 1.5px;
- background-image: linear-gradient(90deg, #F79533 0%, #F37055 15%, #EF4E7B 30%, #A166AB 44%, #5073B8 58%, #1098AD 72%, #07B39B 86%, #6DBA82 100%);
-}
-
-// 页面顶部行高
-.header {
- line-height: 1.5;
-}
-
-// 页面背景色
-.container {
- background-color: rgba(0, 0, 0, 0);
-}
-
-// 表格奇数行
-.highlight table > tbody > tr {
- &:nth-of-type(odd) {
- background-color: #E0EEE0;
- }
-}
-
-// 表格每一行鼠标经过时的颜色
-table > tbody > tr {
- &:hover {
- background-color: #CDC9C9;
- }
-}
-
-// 表格的单元格
-table td, table th {
- border-right: 1px solid #F5F5DC;
-}
-
-// 文章内链接文本样式
-.content p a {
- color: #0593d3;
- border-bottom: none;
- border-bottom: 1px solid #0593d3;
-
- &:hover {
- color: #fc6423;
- border-bottom: none;
- border-bottom: 1px solid #fc6423;
- }
-}
-
-.post {
- font-family: roboto mono, lato, pingfang sc, microsoft yahei, sans-serif;
-}
-
-.post span.exturl {
- color: #0593d3;
- border-bottom: none;
- border-bottom: 1px solid #0593d3;
-
- &:hover {
- color: #fc6423;
- border-bottom: none;
- border-bottom: 1px solid #fc6423;
- }
-}
-
-
-// 代码块 styles.
-code {
- color: #ff7600;
- background: #fbf7f8;
- margin: 4px;
-}
-
-// 大代码块的自定义样式
-.highlight, pre {
- margin: 6px 0;
- padding: 0px;
-}
-
-.highlight {
- border-radius: 5px;
-}
-
-blockquote {
- margin: 6px 0;
- color: #2e405b;
- border-left: 4px solid rgb(16, 152, 173);
- background-color: rgb(227, 242, 253);
- /* border-top-right-radius: 3px;
- border-bottom-right-radius: 3px; */
- margin-top: 20px;
- margin-bottom: 20px;
- border-radius: 3px
-}
-
-//复制按钮
-.copy-btn {
- color: #ff7600;
- background: #fff;
- border: 1px solid #ff7600;
-}
-
-.copy-btn:hover {
- border-color: #ff7600;
- color: #fff;
- background: #ff7600;
-}
-
-.posts-expand {
- padding-top: 10px;
-}
-
-// 文章元数据(meta)留白更改
-.posts-expand .post-meta {
- margin: 10px 0px 20px 0px;
-}
-
-.posts-expand .post-meta time {
- border-bottom: none;
-}
-
-.posts-expand .post-eof {
- width: 30%
-}
-
-.post-category a {
- color: #49b1f5;
-}
-
-.post-category a:hover {
- color: #fc6423;
-}
-
-.post-category a, .post-comments-count a {
- border-bottom: none;
-}
-
-.posts-expand .post.post-type-normal, .post-block {
- border-radius: 10px !important;
-}
-
-// 鼠标移动至文章标题时的效果
-.posts-expand .post-title-link::before {
- content: "";
- position: absolute;
- width: 100%;
- height: 2px;
- bottom: 0;
- left: 0;
- background-color: #49b1f5;
- visibility: hidden;
- -webkit-transform: scaleX(0);
- -moz-transform: scaleX(0);
- -ms-transform: scaleX(0);
- -o-transform: scaleX(0);
- transform: scaleX(0);
- transition-duration: 0.2s;
- transition-timing-function: ease-in-out;
- transition-delay: 0s;
-}
-
-// 文章标题动态效果
-.posts-expand .post-title-link::before {
- background-image: linear-gradient(90deg, #a166ab 0%, #ef4e7b 25%, #f37055 50%, #ef4e7b 75%, #a166ab 100%);
-}
-
-// 文章标题字体
-.posts-expand .post-title {
- font-size: 26px;
- font-weight: 700;
-}
-
-// 文章内标题样式(左边的竖线)
-.content h2, .content h3, .content h4, .content h5, .content h6 {
- border-left: 4px solid #f27c8d;
- //margin-left: -40px;
- padding-left: 32px;
- border-bottom: none;
-}
-
-/* 标题 */
-// 点进文章,文章的主标题
-.posts-expand article:only-of-type h1 {
- font-size: 26px;
-}
-
-// 二级标题(正常显示器分辨率)
-// 其实要在左下角和右下角加上一个折角、翻折的效果(一个三角形)
-.content h2 {
- padding-left: 2%;
- color: #2e405b;
- position: relative;
-}
-
-h2.post-title {
- border: none;
-}
-
-// 三级标题
-.content h3 {
- border-bottom: 1px solid #cfd8dc;
- padding-bottom: 0.3em;
- color: #49b1f5;
- position: relative;
-}
-
-.content h4 {
- color: #555;
- position: relative;
-}
-
-// 二级标题(移动终端分辨率)
-@media (min-width: 768px) and (max-width: 991px) {
- .main .content h2 {
- margin: 20px -23px 15px 0;
- }
-}
-
-@media (max-width: 767px) {
- .main .content h2 {
- margin: 20px -15px 15px 0;
- }
-}
-
-// 时间轴标题左侧不加竖线
-.archive h2, .archive h3, .archive h4, .archive h5, .archive h6 {
- border: none
- //margin-left: -40px;
- padding-left: 0;
-}
-
-// 图片底下的描述性文字
-.content .image-caption, .content .figure .caption {
- text-align: left;
-}
-
-//文章末尾本文链接
-.post-copyright a {
- color: #0593d3;
- border-bottom: none;
- border-bottom: 1px solid #0593d3;
-
- &:hover {
- color: #fc6423;
- border-bottom: none;
- border-bottom: 1px solid #fc6423;
- }
-}
-
-// 文章底部的tags
-.posts-expand .post-tags a {
- border-bottom: none;
- margin-right: 0px;
- font-size: 13px;
- padding: 0px 5px;
- border-radius: 3px;
- transition-duration: 0.2s;
- transition-timing-function: ease-in-out;
- transition-delay: 0s;
-}
-
-.posts-expand .post-tags a:hover {
- background: transparent;
-}
-
-// 文章底部页面跳转
-.post-nav-item a {
- color: #0593d3;
-}
-
-.post-nav-item a:hover {
- color: #fc6423;
- font-weight: bold;
-}
-
-.pagination .prev, .pagination .next, .pagination .page-number {
- border: none;
-}
-
-.pagination .prev:hover, .pagination .next:hover, .pagination .page-number:hover {
- background: #ccc;
- border-top-color: #fff;
-}
-
-// 时间轴左边线条
-.posts-collapse::after {
- margin-left: -2px;
- background-image: linear-gradient(180deg, #f79533 0, #f37055 15%, #ef4e7b 30%, #a166ab 44%, #5073b8 58%, #1098ad 72%, #07b39b 86%, #6dba82 100%);
-}
-
-// 时间轴左边线条圆点颜色
-.posts-collapse .post-header::before {
- background-color: #fff;
- border: 1px solid #bbb8b8;
-}
-
-// 时间轴文章标题左边圆点颜色
-.posts-collapse .post-header:hover::before {
- background-color: #49b1f5;
-}
-
-// archives页面时间轴左边线条第一个圆点颜色
-.page-archive .posts-collapse .archive-move-on {
- top: 10px;
- opacity: 1;
- background-color: rgb(255, 255, 255);
- box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.5);
-}
-
-.posts-collapse .post-header {
- border-bottom: none;
-}
-
-.posts-collapse .collection-title::before {
- opacity: 1;
- background-color: rgb(255, 255, 255);
- box-shadow: 0px 0px 1px 1px #bdbdbd;
-}
-
-// 标签云页面
-.tag-cloud-tags a {
- border-bottom: 2px solid;
-}
-
-.site-state-item {
- border-left: none;
-}
-
-.feed-link {
- border-top: none;
- border-bottom: none;
- text-align: center;
-}
-
-// 自定义的引用样式
-blockquote.question {
- border-left: 4px solid rgb(16, 152, 173);
- background-color: rgb(227, 242, 253);
- border-top-right-radius: 3px;
- border-bottom-right-radius: 3px;
- margin-bottom: 20px;
-}
-
-// 自定义的数字块
-span#inline-toc {
- display: inline-block;
- border-radius: 80% 100% 90% 20%;
- background-color: rgb(227, 242, 253);
- color: #555;
- padding: 0.05em 0.4em;
- margin: 2px 5px 2px 0px;
- line-height: 1.5;
-}
-
-// 浏览器滚动条
-::-webkit-scrollbar {
- width: 6px;
- height: 6px;
-}
-
-::-webkit-scrollbar-thumb {
- background: #49b1f5;
-}
-
-::-webkit-scrollbar-track {
- background-color: transparent;
-}
-
-// 自定义样式
-code {
- margin: 2px;
- background: #fbf7f8;
- color: #ff7600;
-}
-
-.copyright .exturl {
- color: #ff7600;
- border-bottom: 1px solid #ff7600;
-
- &:hover {
- color: #0593d3;
- border-bottom: none;
- border-bottom: 1px solid #0593d3;
- }
-}
-
-.mp-image {
- display: block;
- margin: 0 auto;
- padding: $site-author-image-padding;
- max-width: $site-author-image-width;
- height: $site-author-image-height;
- border: $site-author-image-border-width solid $site-author-image-border-color;
- opacity: hexo-config('avatar.opacity') is a 'unit' ? hexo-config('avatar.opacity'):1;
-}
-
-.inline-img {
- .exturl {
- border: none;
-
- a {
- border: none;
- cursor: pointer;
-
- &:hover {
- border-bottom: none;
- }
- }
-
- .fancybox {
- img {
- display: inline !important;
- box-sizing: border-box;
- margin: 0;
- padding: 0;
- border: none;
- vertical-align: middle;
- }
- }
-
- .nofancybox {
- display: inline !important;
- box-sizing: border-box;
- margin: 0;
- padding: 0;
- border: none;
- vertical-align: middle;
- }
-
- i.fa {
- margin-left: 0;
- font-size: 0;
- }
- }
-}
diff --git a/source/_posts/2016-03-24.ubuntu-15.04-install-jdk.md b/source/_posts/2016-03-24.ubuntu-15.04-install-jdk.md
deleted file mode 100644
index 8c6bb30d..00000000
--- a/source/_posts/2016-03-24.ubuntu-15.04-install-jdk.md
+++ /dev/null
@@ -1,133 +0,0 @@
----
-title: Ubuntu-15.04安装jdk
-comments: true
-fancybox: false
-toc: true
-date: 2016-03-24 00:00:46
-tags:
-- linux
-categories:
-- linux
-description:
-permalink: ubuntu-15.04-install-jdk
-top:
----
-前言 最近在Linux上学习Python,而PyCharm需要java环境的支持,本文记录了在Ubuntu-15.04上安装jdk1.8版本的步骤与遇到的问题。
-
-## 1. 方式一
-
->**Ubuntu安装JDK**
-
->**系统版本:**Ubuntu 15.04 x64
-
->**JDK版本:**jdk-8u60-linux-x64
-
-### 步骤详解
-
-1. 查看系统位数,输入以下命令即可
- `getconf LONG_BIT`
-
-2. 下载对应的JDK文件,我这里下载的是**jdk-8u60-linux-64.tar.gz**
-
-3. 创建目录作为JDK的安装目录,这里选择安装位置为:**/usr/java/**
- `sudo mkdir /usr/java`
-
-4. 解压文件带/usr/java/目录下,文件下载的位置是在download目录下的
- `cd download`
- `sudo tar zxvf jdk-8u60-linux-x64.tar.gz -C /usr/java/`
-
-5. 进入到**/usr/java/**目录,给文件夹改名(这一步并非必须,但是为了配置环境变量方便,建议这样做)
- `cd /usr/java/`
- `sudo mv jdk1.8.0_60 jdk_8u60`
-
-6. 配置系统环境变量
- `sudo gedit /etc/environment`
-
- 加入以下的内容
- `PATH=":$JAVA_HOME/bin"`
- `export CLASSPATH=.:$JAVA_HOME/lib:$JAVA_HOME/jre/lib`
- `export JAVA_HOME=/usr/java/jdk_8u60`
-
- 修改完成后保存并关闭,输入以下命令使环境变量生效
- `source /etc/environment`
-
-7. 使用echo命令检查环境变量的配置情况
- `echo $JAVA_HOME`
- >输出 /usr/java/jdk_8u60
-
- `echo $CLASSPATH`
- >输出 .:/usr/java/jdk_8u60/lib:/usr/java/jdk_8u60/jre/lib
-
- `echo $PATH`
-
- >输出 /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/usr/java/jdk_8u60/bin
-
-8. 查看安装版本:
- >java -version
- >Picked up JAVA_TOOL_OPTIONS: -javaagent:/usr/share/java/jayatanaag.jar
- >java version "1.8.0_60"
- >Java(TM) SE Runtime Environment (build 1.8.0_60-b27)
- >Java HotSpot(TM) 64-Bit Server VM (build 25.60-b23, mixed mode)
-
-9. 设置所有用户的环境变量,不然重启之后依然找不到java命令
- `sudo gedit /etc/profile`
-
- 添加以下内容即可(注意替换路径):
-
- `#set Java environment`
- `JAVA_HOME=/usr/java/jdk_8u60`
- `export JRE_HOME=/usr/java/jdk_8u60/jre`
- `export CLASSPATH=.:$JAVA_HOME/lib:$JRE_HOME/lib:$CLASSPATH`
- `export PATH=$JAVA_HOME/bin:$JRE_HOME/bin:$PATH`
-
-10. 重启电脑,使配置生效。
-
-11. 结束了?没有!!!
-
- >如果使用Myeclipse等IDE的或者安装了多个jdk版本的请继续向下看↓↓↓
-
-12. 修改默认的JDK(注意替换路径)
- `sudo update-alternatives --install "/usr/bin/java" "java" "/usr/java/jdk_8u60/bin/java" 300`
- `sudo update-alternatives --install "/usr/bin/javac" "javac" "/usr/java/jdk_8u60/bin/javac" 300`
- `sudo update-alternatives --install "/usr/bin/javaws" "javaws" "/usr/java/jdk_8u60/bin/javaws" 300`
-
- >以上是为不同的命令建立链接,如果存在多个JDK只需要替换路径重新执行一遍即可建立链接,执行以下命令选择链接,如果每个选项只建立了一个链接,系统不会提供选项,如果存在多个链接,请根据需要选择不同选项
-
- `sudo update-alternatives --config java`
- `sudo update-alternatives --config javac`
- `sudo update-alternatives --config javaws`
-
-## 2. 方式二
-
-1. 如果在 Ubuntu 软件中心安装过 OpenJDK,使用如下命令将其删除:
-`sudo apt-get purge openjdk*`
-
-2. 添加源和更新
-`sudo add-apt-repository ppa:webupd8team/java`
-`sudo apt-get update`
-
-3. 安装java 8
-`sudo apt-get install Oracle-java8-installer`
-
-## 3. 安装时常见错误处理
-
-1. 安装时VirtualBox异常退出,重进时报如下错误:
- **dpkg 被中断,您必须手工运行 sudo dpkg --configure -a 解决此问题。**
- 解决方法:
- `sudo rm /var/lib/dpkg/updates/*`
- `sudo apt-get update`
- `sudo apt-get upgrade`
-
-2. 运行`java -version`出现如下错误:
- **Picked up JAVA_TOOL_OPTIONS: -javaagent:/usr/share/java/jayatanaag.jar。**
- 解决方法:
- `sudo rm /usr/share/upstart/sessions/jayatana.conf`
- `sudo add-apt-repository ppa:danjaredg/jayatana`
- `sudo apt-get update`
- `sudo apt-get install jayatana`
- 再正常运行正常:
- `xkcoding@Yangkai:~$ ~/workspace$ java -version`
- `java version "1.8.0_60"`
- `Java(TM) SE Runtime Environment (build 1.8.0_60-b27)`
- `Java HotSpot(TM) 64-Bit Server VM (build 25.60-b23, mixed mode)`
-
diff --git a/source/_posts/2016-03-25.android-open-project.md b/source/_posts/2016-03-25.android-open-project.md
deleted file mode 100644
index 2f8c09a6..00000000
--- a/source/_posts/2016-03-25.android-open-project.md
+++ /dev/null
@@ -1,2666 +0,0 @@
----
-title: Android 开源项目分类汇总
-comments: true
-fancybox: false
-toc: true
-date: 2016-03-25 22:15:00
-tags:
-- Android
-categories:
-- Android
-description:
-permalink: android-open-project
-top:
----
-## 目前包括:
->1. Android 开源项目第一篇——个性化控件(View)篇
->*包括:ListView、ActionBar、Menu、ViewPager、Gallery、GridView、ImageView、ProgressBar、TextView、ScrollView、TimeView、TipView、FlipView、ColorPickView、GraphView、UI Style、其他*
->1. Android 开源项目第二篇——工具库篇
->*包括:依赖注入、图片缓存、网络请求、数据库 ORM 工具包、Android 公共库、高版本向低版本兼容库、多媒体、事件总线、传感器、安全、插件化、文件、其他*
->1. Android 开源项目第三篇——优秀项目篇
->*比较有意思的完整的 Android 项目*
->1. Android 开源项目第四篇——开发及测试工具篇
->*包括:开发效率工具、开发自测相关、测试工具、开发及编译环境、其他*
->1. Android 开源项目第五篇——优秀个人和团体篇
->*乐于分享并且有一些很不错的开源项目的个人和组织,包括 JakeWharton、Chris Banes、Koushik Dutta 等大牛*
-
-
-### 提示
-以下链接网址若存在不能访问,请自行尝试翻墙,推荐使用GreenVPN
-[](http://gjsq.me/15038648)
-
-## 第一部分 个性化控件(View)
-主要介绍那些不错个性化的 View,包括 ListView、ActionBar、Menu、ViewPager、Gallery、GridView、ImageView、ProgressBar、TextView、ScrollView、TimeView、TipView、FlipView、ColorPickView、GraphView、UI Style 等等。
-
-### 一、ListView
-1. android-pulltorefresh
-一个强大的拉动刷新开源项目,支持各种控件下拉刷新,ListView、ViewPager、WebView、ExpandableListView、GridView、ScrollView、Horizontal ScrollView、Fragment 上下左右拉动刷新,比下面 johannilsson 那个只支持 ListView 的强大的多。并且它实现的下拉刷新 ListView 在 item 不足一屏情况下也不会显示刷新提示,体验更好。
-项目地址:https://github.com/chrisbanes/Android-PullToRefresh
-Demo 地址:https://github.com/Trinea/TrineaDownload/blob/master/pull-to-refreshview-demo.apk?raw=true
-APP 示例:新浪微博各个页面
-
-1. android-pulltorefresh-listview
-下拉刷新 ListView,这个被很多人使用的项目实际有不少 bug,推荐使用 [android-Ultra-Pull-to-Refresh](https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh)
-项目地址:https://github.com/johannilsson/android-pulltorefresh
-Demo 地址:https://github.com/Trinea/TrineaDownload/blob/master/pull-to-refresh-listview-demo.apk?raw=true
-
-3. android-Ultra-Pull-to-Refresh
-下拉刷新,第一个项目已经停止维护了,并且使用起来相对复杂,定制性也差。这个是替代和改良方案。这个继承于 ViewGroup 可以包含任何 View。功能甚至比 SwipeRefreshLayout 强大。使用起来非常简单。良好的设计,如果你想定制自己的 UI 样式,非常简单,就像给 ListView 加一个 Header View 那么简单。支持 `API LEVEL >= 8`
-项目地址:https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh
-原理剖析文档:[android-Ultra-Pull-to-Refresh](http://a.codekk.com/detail/Android/Grumoon/android-Ultra-Pull-To-Refresh 源码解析)
-Demo 地址:https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh/blob/master/ptr-demo/target/ultra-ptr-demo.apk?raw=true
-
-1. Android-PullToRefreshRecyclerView
-支持下拉刷新的RecyclerView,同时支持滑动到底部自动加载数据、给RecyclerView添加Header。并且不更改原有RecyclerView的逻辑。
-项目地址:https://github.com/HomHomLin/Android-PullToRefreshRecyclerView。
-Demo地址:https://github.com/HomHomLin/Android-PullToRefreshRecyclerView/blob/master/sample.apk
-
-1. DragSortListView
-拖动排序的 ListView,同时支持 ListView 滑动 item 删除,各个 Item 高度不一、单选、复选、CursorAdapter 做为适配器、拖动背景变化等
-项目地址:https://github.com/bauerca/drag-sort-listview
-Demo 地址:https://play.google.com/store/apps/details?id=com.mobeta.android.demodslv
-APP 示例:Wordpress Android
-
-1. SwipeListView
-支持定义 ListView 左右滑动事件,支持左右滑动位移,支持定义动画时间
-项目地址:https://github.com/47deg/android-swipelistview
-Demo 地址:https://play.google.com/store/apps/details?id=com.fortysevendeg.android.swipelistview
-APP 示例:微信
-
-1. SlideAndDragListView
-支持ListView的Item的拖动排序、左右滑动事件,可自定义左右滑动显示文字、图标、位移,同时支持onItemClick、onItemLongClick等监听器,提供丰富的回调接口。
-项目地址:https://github.com/yydcdut/SlideAndDragListView
-Demo 地址:https://github.com/yydcdut/SlideAndDragListView/blob/master/apk/sdlv.apk?raw=true
-APP 示例:Android 手机QQ 5.0
-
-1. Android-SwipeToDismiss
-滑动 Item 消失 ListView,支持 3.0 以下版本见:https://github.com/JakeWharton/SwipeToDismissNOA
-项目地址:https://github.com/romannurik/Android-SwipeToDismiss
-Demo 地址:https://github.com/JakeWharton/SwipeToDismissNOA/SwipeToDismissNOA.apk/qr_code
-
-1. RecyclerViewSwipeDismiss
-轻量级支持 support-v7 中的 RecyclerView 的滑动删除(Swipe to dismiss)行为,不需要修改源代码,只要简单的绑定`onTouchListener`
-项目地址:https://github.com/CodeFalling/RecyclerViewSwipeDismiss
-
-1. PinnedHeaderExpandableListView
-首先它是一个 ExpandableListView,但是它的头部可以固定,其次,在它的上面还有一个头部可以来回伸缩
-项目地址:https://github.com/singwhatiwanna/PinnedHeaderExpandableListView
-APP 示例:百度手机卫士垃圾清理界面
-
-1. StickyListHeaders
-GroupName 滑动到顶端时会固定不动直到另外一个 GroupName 到达顶端的 ExpandListView,支持快速滑动,支持 Android2.3 及以上
-项目地址:https://github.com/emilsjolander/StickyListHeaders
-APP 示例:Android 4.0 联系人
-
-1. pinned-section-listview
-GroupName 滑动到顶端时会固定不动直到另外一个 GroupName 到达顶端的 ExpandListView
-项目地址:https://github.com/beworker/pinned-section-listview
-
-1. PinnedHeaderListView
-GroupName 滑动到顶端时会固定不动直到另外一个 GroupName 到达顶端的 ExpandListView
-项目地址:https://github.com/JimiSmith/PinnedHeaderListView
-
-1. QuickReturn
-ListView/ScrollView 的 header 或 footer,当向下滚动时消失,向上滚动时出现
-项目地址:https://github.com/lawloretienne/QuickReturn
-Demo 地址:https://play.google.com/store/apps/details?id=com.etiennelawlor.quickreturn
-
-1. QuickReturnHeader
-ListView/ScrollView 的 header 或 footer,当向下滚动时消失,向上滚动时出现
-项目地址:https://github.com/ManuelPeinado/QuickReturnHeader
-Demo 地址:https://github.com/Trinea/TrineaDownload/blob/master/quick-return-header-demo.apk?raw=true
-APP 示例:google plus
-
-1. IndexableListView
-ListView 右侧会显示 item 首字母快捷索引,点击可快速滑动到某个 item
-项目地址:https://github.com/woozzu/IndexableListView
-Demo 地址:https://github.com/Trinea/TrineaDownload/blob/master/indexable-listview.apk?raw=true
-APP 示例:微信通讯录、小米联系人
-
-1. CustomFastScrollView
-ListView 快速滑动,同时屏幕中间 PopupWindows 显示滑动到的 item 内容或首字母
-项目地址:https://github.com/nolanlawson/CustomFastScrollViewDemo
-
-1. Android-ScrollBarPanel
-ListView 滑动时固定的 Panel 指示显示在 scrollbar 旁边
-项目地址:https://github.com/rno/Android-ScrollBarPanel
-
-1. SlideExpandableListView
-用户点击 listView item 滑出固定区域,其他 item 的区域收缩
-项目地址:https://github.com/tjerkw/Android-SlideExpandableListView
-Demo 地址:https://github.com/Trinea/TrineaDownload/blob/master/slide-expandable-listView-demo.apk?raw=true
-
-1. JazzyListView
-ListView 及 GridView item 以特殊动画效果进入屏幕,效果包括 grow、cards、curl、wave、flip、fly 等等
-项目地址:https://github.com/twotoasters/JazzyListView
-Demo 地址:https://play.google.com/store/apps/details?id=com.twotoasters.jazzylistview.sample
-在线演示:http://lab.hakim.se/scroll-effects/
-
-1. ListViewAnimations
-带 Item 显示动画的 ListView,动画包括底部飞入、其他方向斜飞入、下层飞入、渐变消失、滑动删除等
-项目地址:https://github.com/nhaarman/ListViewAnimations
-Demo 地址:https://play.google.com/store/apps/details?id=com.haarman.listviewanimations
-APP 示例:Google plus、Google Now 卡片式进入、小米系统中应用商店、联系人、游戏中心、音乐、文件管理器的 ListView、Ultimate、Light Flow Lite、TreinVerkeer、Running Coach、Pearl Jam Lyrics、Calorie Chart、Car Hire、Super BART、DK FlashCards、Counter Plus、Voorlees Verhaaltjes 2.0
-
-1. DevsmartLib-Android
-横向 ListView
-项目地址:https://github.com/dinocore1/DevsmartLib-Android
-Demo 地址:https://github.com/Trinea/TrineaDownload/blob/master/horizontal-listview-demo.apk?raw=true
-
-1. TwoWayView
-横向 ListView 的效果,继承自 AdapterView
-项目地址:https://github.com/lucasr/twoway-view
-
-1. HorizontalVariableListView
-支持 Item 宽度不一致的 ListView
-项目地址:https://github.com/sephiroth74/HorizontalVariableListView
-
-1. LinearListView
-用 LinearLayout 实现的 ListView,可解决多个 ListView 并存等问题。目前自己也有需要,等亲自尝试过后会再具体介绍
-项目地址:https://github.com/frankiesardo/LinearListView
-
-1. MultiChoiceAdapter
-支持多选的 ListView Adapter
-项目地址:https://github.com/ManuelPeinado/MultiChoiceAdapter
-Demo 地址:https://play.google.com/store/apps/details?id=com.manuelpeinado.multichoiceadapter.demo
-
-1. EnhancedListView
-支持横向滑动删除列表项以及撤销删除的 ListView,该项目的前身是[SwipeToDismissUndoList](https://github.com/timroes/SwipeToDismissUndoList)
-项目地址:https://github.com/timroes/EnhancedListView
-Demo 地址:https://play.google.com/store/apps/details?id=de.timroes.android.listviewdemo&rdid=de.timroes.android.listviewdemo
-
-1. ListBuddies
-自动滚动的双列 ListView ,两个 ListView 滚动速度不一致,有视差效果
-项目地址:https://github.com/jpardogo/ListBuddies
-Demo 地址:https://play.google.com/store/apps/details?id=com.jpardogo.android.listbuddies
-
-1. SwipeMenuListView
-针对 ListView item 的侧滑菜单
-项目地址:https://github.com/baoyongzhang/SwipeMenuListView
-APP 示例:手机 QQV5.0
-
-1. PagingListView
-分页加载的 ListView。当滑动到 ListView 底部最后一个元素时,显示一个进度行,然后加载下一页数据,并显示。
-项目地址:https://github.com/nicolasjafelle/PagingListView
-
-1. PullZoomView
-支持下拉时 HeaderView 缩放的 ListView、ScrollView
-项目地址:https://github.com/Frank-Zhu/PullZoomView
-
-1. PullToZoomInListView
-滑动 ListView 时使其 HeaderView 跟随滑动缩放
-项目地址:https://github.com/matrixxun/PullToZoomInListView
-
-1. CalendarListview
-实现每个月一行日历效果的 ListView
-项目地址:https://github.com/traex/CalendarListview
-原理剖析文档:[CalendarListview](http://a.codekk.com/detail/Android/Rogary/CalendarListView%20%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90)
-
-1. sticky-headers-recyclerview
-GroupName 滑动到顶端时会固定不动直到另外一个 GroupName 到达顶端的 ListView,采用 support-v7 中的 RecyclerView 实现
-项目地址:https://github.com/timehop/sticky-headers-recyclerview
-
-1. PullSeparateListView
-到达顶部或底部继续拉动时,实现 Item 间的相互分离,两种模式:(1) 全部分离的模式,即屏幕内所有 Item 都会分离 (2)部分分离模式,以点击位置为分界点,部分 item 分离
-项目地址:https://github.com/chiemy/PullSeparateListView
-
-1. ExpandableLayout
-Header 和 Content Item 都可以展开的 ExpandableListview
-项目地址:https://github.com/traex/ExpandableLayout
-
-1. PagedHeadListView
-支持 paginated header 以及 material page indicator 的 ListView.
-项目地址:https://github.com/JorgeCastilloPrz/PagedHeadListView
-
-1. CustomSwipeListView
-支持左滑弹出自定义菜单,右滑删除且允许撤销,同时可以自定义滑动动画时间和滑动触发事件的时机等。
-项目地址:https://github.com/xyczero/Android-CustomSwipeListView
-Demo 地址:[Download here](http://7u2jsw.com1.z0.glb.clouddn.com/githubCustomSwipeListView.apk)
-
-1. Pull-to-Refresh.Rentals-Android
-提供一个简单可以自定义的下拉刷新实现,[Yalantis](http://yalantis.com/) 出品。
-项目地址:https://github.com/Yalantis/Pull-to-Refresh.Rentals-Android
-
-1. ScrollerCalendar
-实现每行显示一年的 12 个月份的 RecyclerView 年历
-项目地址:https://github.com/guanchao/ScrollerCalendar
-
-1. ExtractWordView
-一个可以提取单词的 ListView,支持"放大镜"效果。
-项目地址:https://github.com/jcodeing/ExtractWordView
-Demo 地址:[Download here](https://raw.githubusercontent.com/jcodeing/ExtractWordView/master/ExtractWordView-demo.apk)
-
-1. FlyRefresh
-支持 ListView, GridView, RecyclerView, ScrollView 的下拉刷新
-项目地址:https://github.com/race604/FlyRefresh
-
-1. MVCHelper
-实现下拉刷新,滚动底部自动加载更多,分页加载,自动切换显示网络失败布局,暂无数据布局,支持任意 view,真正的 MVC 架构
-支持切换主流下拉刷新框架 Android-PullToRefresh-Library,android-Ultra-Pull-To-Refresh-library,SwipeRefreshLayout
-项目地址:https://github.com/LuckyJayce/MVCHelper
-Demo 地址:https://github.com/LuckyJayce/MVCHelper/blob/master/raw/MVCHelper_Demo.apk?raw=true
-
-1. RecyclerViewSwipeDismiss
-支持滑动 Item 操作、点击展开、拖动排序、展开后拖动排序等特性的 RecyclerView
-项目地址:https://play.google.com/store/apps/details?id=com.h6ah4i.android.example.advrecyclerview
-视频:http://www.youtube.com/watch?feature=player_embedded&v=S7cSwMArjUQ
-
-1. WaterDropListView
-模仿 iOS 下拉刷新“水滴”效果,支持下拉刷新和上拉加载
-项目地址:https://github.com/THEONE10211024/WaterDropListView
-
-1. PopupListView
-實現ListItem 點擊後置頂並可顯示客製化添加的item的內部View 的ListView
-项目地址:https://github.com/s8871404/PopupListView
-Demo 地址:https://play.google.com/store/apps/details?id=com.baobomb.popuplistview_sample
-
-1. CircleRefreshLayout
-一个包含有趣的动画的自定义下拉刷新布局
-项目地址:https://github.com/tuesda/CircleRefreshLayout
-
-1. EasyRecyclerView
-一款简单易用的EasyRecyclerView
-1.提供EasyRecyclerView,不需要写太多RecyclerView的配置代码
-2.提供好一个可以适配单布局和多布局的RecyclerViewAdapter
-3.提供了分割线的实现类,只需要传入一个DrawableId
-项目地址:https://github.com/CaMnter/EasyRecyclerView
-
-1. CanRefresh
-可适配所有视图的下拉刷新上拉加载,并支持各种风格
-项目地址:https://github.com/canyinghao/CanRefresh
-
-### 二、ActionBar
-1. ActionBarSherlock
-为 Android 所有版本提供统一的 ActionBar,解决 4.0 以下 ActionBar 的适配问题
-项目地址:https://github.com/JakeWharton/ActionBarSherlock
-Demo 地址:https://play.google.com/store/apps/details?id=com.actionbarsherlock.sample.demos
-标签:兼容库, ActionBar
-
-1. ActionBar-PullToRefresh
-下拉刷新,ActionBar 出现加载中提示
-项目地址:https://github.com/chrisbanes/ActionBar-PullToRefresh
-Demo 地址:https://play.google.com/store/apps/details?id=uk.co.senab.actionbarpulltorefresh.samples.stock
-APP 示例:Gmail,Google Plus,知乎等
-
-1. FadingActionBar
-ListView 向下滚动逐渐显现的 ActionBar
-项目地址:https://github.com/ManuelPeinado/FadingActionBar
-Demo 地址:https://play.google.com/store/apps/details?id=com.manuelpeinado.fadingactionbar.demo
-APP 示例:google music,知乎
-
-1. NotBoringActionBar
-google music 下拉收缩的 ActionBar
-项目地址:https://github.com/flavienlaurent/NotBoringActionBar
-Demo 地址:http://flavienlaurent.com/blog/2013/11/20/making-your-action-bar-not-boring/
-APP 示例:Google 音乐
-
-1. RefreshActionItem
-带进度显示和刷新按钮的 ActionBar
-项目地址:https://github.com/ManuelPeinado/RefreshActionItem
-Demo 地址:https://play.google.com/store/apps/details?id=com.manuelpeinado.refreshactionitem.demo
-APP 示例:The New York Times,DevAppsDirect
-
-1. GlassActionBar
-类似玻璃的有一定透明度的 ActionBar
-项目地址:https://github.com/ManuelPeinado/GlassActionBar
-Demo 地址:https://play.google.com/store/apps/details?id=com.manuelpeinado.glassactionbardemo
-APP 示例:google music
-
-### 三、Menu
-1. MenuDrawer
-滑出式菜单,通过拖动屏幕边缘滑出菜单,支持屏幕上下左右划出,支持当前 View 处于上下层,支持 Windows 边缘、ListView 边缘、ViewPager 变化划出菜单等。
-项目地址:https://github.com/SimonVT/android-menudrawer
-Demo 地址:http://simonvt.github.io/android-menudrawer/
-APP 示例:Gmail、Google Music 等大部分 google app
-
-1. SlidingMenu
-滑出式菜单,通过拖动屏幕边缘滑出菜单,支持屏幕左右划出,支持菜单 zoom、scale、slide up 三种动画样式出现。与 MenuDrawer 相比而言,SlidingMenu 支持菜单动画样式出现,MenuDrawer 支持菜单 view 处于内容的上下层
-项目地址:https://github.com/jfeinstein10/SlidingMenu
-原理剖析文档:[SlidingMenu](http://a.codekk.com/detail/Android/huxian99/SlidingMenu 源码解析)
-Demo 地址:https://play.google.com/store/apps/details?id=com.slidingmenu.example
-APP 示例:Foursquare, LinkedIn, Zappos, Rdio, Evernote Food, Plume, VLC for Android, ESPN ScoreCenter, MLS MatchDay, 9GAG, Wunderlist 2, The Verge, MTG Familiar, Mantano Reader, Falcon Pro (BETA), MW3 Barracks
-
-1. ArcMenu
-支持类似 Path 的左下角动画旋转菜单及横向划出菜单、圆心弹出菜单
-项目地址:https://github.com/daCapricorn/ArcMenu
-https://dl.dropboxusercontent.com/u/11369687/preview1.png
-https://dl.dropboxusercontent.com/u/11369687/raymenu.png
-APP 示例:Path
-
-1. android-satellite-menu
-类似 Path 的左下角动画旋转菜单
-项目地址:https://github.com/siyamed/android-satellite-menu
-Demo 地址:https://github.com/Trinea/TrineaDownload/blob/master/satellite-menu-demo.apk?raw=true
-APP 示例:Path
-
-1. radial-menu-widget
-圆形菜单,支持二级菜单
-项目地址:https://code.google.com/p/radial-menu-widget/
-
-1. Android Wheel Menu
-圆形旋转选取菜单
-项目地址:https://github.com/anupcowkur/Android-Wheel-Menu
-
-1. FoldingNavigationDrawer
-滑动并以折叠方式打开菜单
-项目地址:https://github.com/tibi1712/FoldingNavigationDrawer-Android
-Demo 地址:https://play.google.com/store/apps/details?id=com.ptr.folding.sample
-
-1. AndroidResideMenu
-仿 Dribbble 的边栏菜单
-项目地址:https://github.com/SpecialCyCi/AndroidResideMenu
-
-1. CircularFloatingActionMenu
-一个可定制的圆形的浮动菜单控件,类似于 Path 的圆形菜单。这个控件的可定制性更强,可以很容易的定制菜单出现消失时的动画,起始角度和半径。
-项目地址:https://github.com/oguzbilgener/CircularFloatingActionMenu
-原理剖析文档:[CircularFloatingActionMenu](http://a.codekk.com/detail/Android/cpacm/CircularFloatingActionMenu 源码解析)
-Demo 地址:https://github.com/oguzbilgener/CircularFloatingActionMenu/tree/master/samples
-
-1. NavigationDrawerSI
-Navigation Drawer 的一个简单实现,滑动并以折叠方式打开菜单
-项目地址:https://github.com/mmBs/NavigationDrawerSI
-Demo 地址:https://play.google.com/store/apps/details?id=mmbialas.pl.navigationdrawersi
-APP 示例:https://play.google.com/store/apps/details?id=mmbialas.pl.navigationdrawersi
-
-1. DragLayout
-使用 support.v4 包下的 ViewDragHelper 实现 QQ5.0 侧滑
-项目地址:https://github.com/BlueMor/DragLayout
-
-1. LDrawer
-Material Design 形式的展开折叠 Icon
-项目地址:https://github.com/ikimuhendis/LDrawer
-
-1. Floating Action Button
-悬浮的圆形菜单栏,支持组建滚动时自动隐藏及其他设置
-项目地址:https://github.com/shamanland/floating-action-button
-
-1. Side-Menu.Android
-分类侧滑菜单,[Yalantis](http://yalantis.com/) 出品。
-项目地址:https://github.com/Yalantis/Side-Menu.Android
-原理剖析文档:[Side-Menu.Android](http://a.codekk.com/detail/Android/cpacm/Side Menu.Android 源码解析)
-
-1. Context-Menu.Android
-可以方便快速集成漂亮带有动画效果的上下文菜单,[Yalantis](http://yalantis.com/)出品。
-项目地址:https://github.com/Yalantis/Context-Menu.Android
-
-1. Droppy
-支持多种样式的下拉菜单
-项目地址:https://github.com/shehabic/Droppy
-
-1. MaterialDrawer
-Material Design 风格的导航抽屉,提供简便且强大的定制功能
-项目地址:https://github.com/mikepenz/MaterialDrawer
-Demo 地址:https://play.google.com/store/apps/details?id=com.mikepenz.materialdrawer.app
-
-1. SlideBottomPanel
-底部划出菜单,滑动时背景图透明度渐变,支持嵌套 LiewView 或 ScrollView
-项目地址:[https://github.com/kingideayou/SlideBottomPanel](https://github.com/kingideayou/SlideBottomPanel)
-Demo 地址:[SlideBottomPanelDemo 下载](https://github.com/kingideayou/SlideBottomPanel/raw/master/apk/SlideBottomPanelDemo.apk)
-
-1. FlowingDrawer
-带手势流动效果侧滑菜单
-项目地址:[https://github.com/mxn21/FlowingDrawer](https://github.com/mxn21/FlowingDrawer)
-
-### 四、ViewPager 、Gallery
-1. AdvancedPagerSlidingTabStrip
-一个完美兼容ViewPager的导航栏组件;可以自定义TabView;能动态加载Tab上的Icon图片; 能显示Tab的消息数量和提示小圆点。
-项目地址:https://github.com/HomHomLin/AdvancedPagerSlidingTabStrip
-
-1. ConvenientBanner
-通用的广告栏控件,让你轻松实现广告头效果。支持无限循环,可以设置自动翻页和时间(而且非常智能,手指触碰则暂停翻页,离开自动开始翻页。你也可以设置在界面onPause的时候不进行自动翻页,onResume之后继续自动翻页),并且提供多种翻页特效。 对比其他广告栏控件,大多都需要对源码进行改动才能加载网络图片,或者帮你集成不是你所需要的图片缓存库。而这个库能让有代码洁癖的你欢喜,不需要对库源码进行修改你就可以使用任何你喜欢的网络图片库进行配合。
-项目地址:https://github.com/saiwu-bigkoo/Android-ConvenientBanner
-
-1. Android-ViewPagerIndicator
-配合 ViewPager 使用的 Indicator,支持各种位置和样式
-项目地址:https://github.com/JakeWharton/Android-ViewPagerIndicator
-Demo 地址:https://play.google.com/store/apps/details?id=com.viewpagerindicator.sample
-APP 示例:太多了。。
-
-1. JazzyViewPager
-支持 Fragment 切换动画的 ViewPager,动画包括转盘、淡入淡出、翻页、层叠、旋转、方块、翻转、放大缩小等,效果类似桌面左右切换的各种效果,不过桌面并非用 ViewPager 实现而已
-项目地址:https://github.com/jfeinstein10/JazzyViewPager
-Demo 地址:https://github.com/jfeinstein10/JazzyViewPager/blob/master/JazzyViewPager.apk?raw=true
-
-1. JellyViewPager
-特殊切换动画的 ViewPager
-项目地址:https://github.com/chiemy/JellyViewPager
-
-1. Android-DirectionalViewPager
-支持横向和纵向(垂直)的 ViewPager
-项目地址:https://github.com/JakeWharton/Android-DirectionalViewPager
-Demo 地址:https://market.android.com/details?id=com.directionalviewpager.sample
-
-1. FancyCoverFlow
-支持 Item 切换动画效果的类似 Gallery View
-项目地址:https://github.com/davidschreiber/FancyCoverFlow
-Demo 地址:https://play.google.com/store/apps/details?id=at.technikum.mti.fancycoverflow.samples
-
-1. AndroidTouchGallery
-支持双击或双指缩放的 Gallery(用 ViewPager 实现),相比下面的 PhotoView,在被放大后依然能滑到下一个 item,并且支持直接从 url 和文件中获取图片,
-项目地址:https://github.com/Dreddik/AndroidTouchGallery
-Demo 地址:https://github.com/Trinea/TrineaDownload/blob/master/touch-gallery-demo.apk?raw=true
-APP 示例:类似微信中查看聊天记录图片时可双击放大,并且放大情况下能正常左右滑动到前后图片
-
-1. Android Auto Scroll ViewPager
-Android 自动滚动 轮播循环的 ViewPager
-项目地址:https://github.com/Trinea/android-auto-scroll-view-pager
-Demo 地址:https://play.google.com/store/apps/details?id=cn.trinea.android.demo
-文档介绍:http://www.trinea.cn/android/auto-scroll-view-pager/
-
-1. Android PagerSlidingTabStrip
-配合 ViewPager 使用的 Indicator,支持 ViewPager Scroll 时 Indicator 联动
-项目地址:https://github.com/astuetz/PagerSlidingTabStrip
-原理剖析文档:[Android PagerSlidingTabStrip](http://a.codekk.com/detail/Android/ayyb1988/PagerSlidingTabStrip 源码解析)
-Demo 地址:https://play.google.com/store/apps/details?id=com.astuetz.viewpager.extensions.sample
-
-1. ViewPager3D
-ViewPager3D 效果
-项目地址:https://github.com/inovex/ViewPager3D
-
-1. AnimaTabsview
-仿网易云音乐标签切换的动画,带透明小三角
-项目地址:https://github.com/wuyexiong/transparent-over-animtabsview
-在线演示:http://v.youku.com/v_show/id_XNzA4MjY5NjA0.html
-
-1. LoopingViewPager
-无限循环的 ViewPager
-项目地址:https://github.com/imbryk/LoopingViewPager
-
-1. android_page_curl
-翻书卷曲效果
-项目地址:https://github.com/harism/android_page_curl
-在线演示:https://www.youtube.com/watch?v=iwu7P5PCpsw
-APP 示例:iReader
-
-1. ViewPagerIndicator
-简化并实现 android 的 TabHost 效果,顶部滑动 tab,引导页,支持自定义 tab 样式,自定义滑动块样式和位置,自定义切换 tab 的过渡动画,子界面的预加载和界面缓存,设置界面是否可滑动
-项目地址:https://github.com/LuckyJayce/ViewPagerIndicator
-
-1. ScreenSlideIndicator
-轻量级的圆形 Indicadtor,位置可以自由调整,不会对 ViewPager 产生任何影响。
-项目地址:[ScreenSlidePager](https://github.com/LyndonChin/Android-ScreenSlidePager)
-
-1. SmartTabLayout
-自定义的 Tab title strip,基于 Google Samples 中的 android-SlidingTabBasic 项目,滑动时 Indicator 可平滑过渡。
-项目地址:https://github.com/ogaclejapan/SmartTabLayout
-Demo 地址:https://play.google.com/store/apps/details?id=com.ogaclejapan.smarttablayout.demo
-
-1. AndroidImageSlider
-Android 图片滑动
-项目地址:https://github.com/daimajia/AndroidImageSlider
-App示例:https://github.com/daimajia/AndroidImageSlider/releases/download/v1.0.8/demo-1.0.8.apk
-
-1. RecyclerViewPager
-完全继承自 RecyclerView,可以自定义触发翻页的距离,可自定义翻页速度,支持垂直方向的 ViewPager,支持 Fragment。
-项目地址:[RecyclerViewPager](https://github.com/lsjwzh/RecyclerViewPager)
-
-1. CircleIndicator
-轻量级ViewPager指示器,支持三种不同的模式
-项目地址:[CircleIndicator]( https://github.com/THEONE10211024/CircleIndicator)
-
-1. EasySlidingTabs
-一款简单、易用的滑动标签页
-项目地址: https://github.com/CaMnter/EasySlidingTabs
-
-### 五、GridView
-1. StaggeredGridView
-允许非对齐行的 GridView,类似 Pinterest 的瀑布流,并且跟 ListView 一样自带 View 缓存,继承自 ViewGroup
-项目地址:https://github.com/maurycyw/StaggeredGridView
-Demo 地址:https://github.com/Trinea/TrineaDownload/blob/master/staggered-gridview-demo.apk?raw=true
-APP 示例:Pinterest 等
-
-1. AndroidStaggeredGrid
-允许非对齐行的 GridView,类似 Pinterest 的瀑布流,继承自 AbsListView
-项目地址:https://github.com/etsy/AndroidStaggeredGrid
-APP 示例:Pinterest 等
-
-1. PinterestLikeAdapterView
-允许非对齐行的 GridView,类似 Pinterest 的瀑布流,允许下拉刷新
-项目地址:https://github.com/GDG-Korea/PinterestLikeAdapterView
-APP 示例:Pinterest 等
-
-1. DraggableGridView
-Item 可拖动交换位置的 GridView,实际是自己继承 ViewGroup 实现,类似桌面的单屏效果,可屏幕自动上下滚动进行 Item 移动交换,多屏效果见下面 PagedDragDropGrid
-项目地址:https://github.com/thquinn/DraggableGridView
-Demo 地址:https://github.com/thquinn/DraggableGridView/blob/master/bin/DraggableGridViewSample.apk?raw=true
-也可以自定义item的宽高和每行的个数,并且修改交换逻辑,当移动到另一个item时就进行交换,删除滚动逻辑。
-项目地址:[DraggableGridView](https://github.com/andyken/DraggableGridView)
-
-1. StickyGridHeaders
-GroupName 滑动到顶端时会固定不动直到另外一个 GroupName 到达顶端的 GridView
-项目地址:https://github.com/TonicArtos/StickyGridHeaders
-
-1. PagedDragDropGrid
-Item 可拖动交换位置、拖动删除的自定义控件,实际是自己继承 ViewGroup 实现,类似桌面的多屏效果,可拖动到屏幕边缘,屏幕自动左右滚动进行 Item 移动交换,可拖动进行删除,单屏效果见上面 DraggableGridView
-项目地址:https://github.com/mrKlar/PagedDragDropGrid
-在线演示:http://youtu.be/FYTSRfthSuQ
-
-1. Android-DraggableGridViewPager
-Item 可拖动交换位置的 GridView,实际是自己继承 ViewGroup 实现,类似桌面的多屏效果,可屏幕自动左右滚动进行 Item 移动交换,单屏效果见上面 DraggableGridView
-项目地址:https://github.com/zzhouj/Android-DraggableGridViewPager
-Demo 地址:https://github.com/Trinea/trinea-download/blob/master/draggable-grid-viewpager-demo.apk?raw=true
-
-1. TwoWayGridView
-可横向滚动的 GridView
-项目地址:https://github.com/jess-anders/two-way-gridview
-
-1. PagingGridView
-分页加载的 GridView。当滑动到 GridView 底部最后一个行时,显示一个进度行,然后加载下一页数据,并显示。
-项目地址:https://github.com/nicolasjafelle/PagingGridView
-
-1. AsymmetricGridView
-一个支持跨行和跨列可变 Item 大小的 GridView
-项目地址:https://github.com/felipecsl/AsymmetricGridView
-Demo 地址:https://play.google.com/store/apps/details?id=com.felipecsl.asymmetricgridview.app
-
-1. GridView with Header and Footer
-和`ListView`一样带头部和底部的`GridView`,用法和`ListView`一样
-项目地址:https://github.com/liaohuqiu/android-GridViewWithHeaderAndFooter
-
-### 六、ImageView
-1. PhotoView
-支持双击或双指缩放的 ImageView,在 ViewPager 等 Scrolling view 中正常使用,相比上面的 AndroidTouchGallery,不仅支持 ViewPager,同时支持单个 ImageView
-项目地址:https://github.com/chrisbanes/PhotoView
-原理剖析文档:[PhotoView](http://a.codekk.com/detail/Android/dkmeteor/PhotoView 源码解析)
-Demo 地址:https://play.google.com/store/apps/details?id=uk.co.senab.photoview.sample
-APP 示例:photup
-
-1. Fresco-ImageView
-Fresco-ImageView是一种Android平台的图像控件,底层是对Fresco的封装,可以异步加载网络图片、项目资源和本地图片,并且支持双指缩放、图片的基本处理以及Fresco的所有特性。
-项目地址:https://github.com/HomHomLin/FrescoImageView。
-
-1. android-gif-drawable
-支持 gif 显示的 view,用 jni 实现的,编译生成 so 库后直接 xml 定义 view 即可,而且本身不依赖于其他开源项目所以相对下面的 ImageViewEx 简单的多
-项目地址:https://github.com/koral--/android-gif-drawable
-
-1. ImageViewEx
-支持 Gif 显示的 ImageView,依赖很多,编译过程很繁琐
-项目地址:https://github.com/frapontillo/ImageViewEx
-Demo 地址:https://github.com/Trinea/TrineaDownload/blob/master/imageviewex-demo.apk?raw=true
-
-1. RoundedImageView
-带圆角的 ImageView
-项目地址:https://github.com/vinc3m1/RoundedImageView
-
-1. SelectableRoundedImageView
-ImageView 允许四个角的每一个有不同的半径值。也允许椭圆形、圆形的形状或者边
-项目地址:https://github.com/pungrue26/SelectableRoundedImageView
-Demo 地址:https://play.google.com/store/apps/details?id=com.joooonho
-
-1. ColorArt
-根据图片的均色设置背景色显示文字和图片,类似 itune11 中效果
-项目地址:https://github.com/MichaelEvans/ColorArt
-Demo 地址:https://github.com/Trinea/TrineaDownload/blob/master/color-art-demo.apk?raw=true
-
-1. CircleImageView
-圆形的 ImageView
-项目地址:https://github.com/hdodenhof/CircleImageView
-
-1. ImageViewZoom
-支持放大和平移的 ImageView
-项目地址:https://github.com/sephiroth74/ImageViewZoom
-APP 示例:https://play.google.com/store/apps/details?id=com.aviary.android.feather
-
-1. KenBurnsView
-实现 Ken Burns effect 效果,达到身临其境效果的 ImageView
-项目地址:https://github.com/flavioarfaria/KenBurnsView
-
-1. CustomShapeImageView
-各种形状的 ImageView, 相比上面的圆形 ImageView,多了更多形状
-项目地址:https://github.com/MostafaGazar/CustomShapeImageView
-
-1. Shape Image View
-可以自定义各种形状的 ImageView, 并且支持边框
-项目地址:https://github.com/siyamed/android-shape-imageview
-
-1. TextDrawable
-一个用于生成带有文本或者字母的图片的轻量级库。扩展自 Drawable,因此可用于现有/自定义/网络等 ImageView 类,并且包含一个流接口用于创建 drawables 以及一个定制的 ColorGenerator
-项目地址:https://github.com/amulyakhare/TextDrawable
-
-1. android-smart-image-view
-可从 URL 或 contact address book 加载图片的 ImageView,支持缓存
-项目地址:https://github.com/loopj/android-smart-image-view
-
-1. PhotoView
-支持双指/双击缩放的ImageView,支持从一个PhotoView缩放到另外一个PhotoView(点击图片放大预览),相对于其他PhototView有更加平滑的缩放,平移的动画,并且支持所有的ScaleType,可以作为普通的ImageView使用
-项目地址:https://github.com/bm-x/PhotoView
-
-### 七、ProgressBar
-1. SmoothProgressBar
-水平进度条
-项目地址:https://github.com/castorflex/SmoothProgressBar
-Demo 地址:https://play.google.com/store/apps/details?id=fr.castorflex.android.smoothprogressbar.sample
-
-1. MaterialProgessBar
-Material Design 的进度条,支持 Android 4.0
-项目地址:https://github.com/DreaminginCodeZH/MaterialProgressBar
-Demo 地址:https://github.com/DreaminginCodeZH/MaterialProgressBar/blob/master/dist/sample.apk
-
-1. ProgressWheel
-支持进度显示的圆形 ProgressBar
-项目地址:https://github.com/Todd-Davies/ProgressWheel
-Demo 地址:https://github.com/Trinea/TrineaDownload/blob/master/progress-wheel-demo.apk?raw=true
-
-1. android-square-progressbar
-在图片周围显示进度
-项目地址:https://github.com/mrwonderman/android-square-progressbar
-Demo 地址:https://play.google.com/store/apps/details?id=net.yscs.android.square_progressbar_example
-APP 示例:square
-
-1. HoloCircularProgressBar
-Android4.1 时钟 App 样式
-项目地址:https://github.com/passsy/android-HoloCircularProgressBar
-APP 示例:Android4.1 时钟 App
-
-1. ProgressButton
-通过图钉的不同状态显示进度
-项目地址:https://github.com/f2prateek/progressbutton
-文档介绍:http://f2prateek.com/progressbutton/
-
-1. GoogleProgressBar
-类似 google 多个圆形卡片翻转的 progressBar
-项目地址:https://github.com/jpardogo/GoogleProgressBar
-
-1. TH-ProgressButton
-带圆形进度显示的按钮
-项目地址:https://github.com/torryharris/TH-ProgressButton
-
-1. NumberProgressBar
-带数字进度的进度条
-项目地址:https://github.com/daimajia/NumberProgressBar
-
-1. CircularProgressDrawable
-带圆形进度显示的进度条
-项目地址:https://github.com/Sefford/CircularProgressDrawable
-
-1. Android-RoundCornerProgressBar
-Android 圆角 ProgressBar,可自定义圆角颜色和半径,包括带 Icon 和不带 Icon 两种类型。
-项目地址:https://github.com/akexorcist/Android-RoundCornerProgressBar
-
-1. circular-progress-button
-带进度显示的 Button
-项目地址:https://github.com/dmytrodanylyk/circular-progress-button
-
-1. WaveView
-一个波纹效果的 View,可用来做 ProgressBar
-项目地址:https://github.com/john990/WaveView
-Demo 地址:https://raw.github.com/john990/WaveView/master/screenshot%26apk/demo.unaligned.apk
-
-1. MaterialLoadingProgressBar
-抽取自 SwipeRefreshLayout 的 Material Design 进度指示器
-项目地址:https://github.com/lsjwzh/MaterialLoadingProgressBar
-
-1. MetaballLoading
-A 2d metaball loading
-项目地址:https://github.com/dodola/MetaballLoading
-
-1. SectorProgressView
-一个圆形或环形的进度显示控件,用圆中扇形的角度大小来表示进度信息
-项目地址:https://github.com/timqi/SectorProgressView
-Demo:[apk download](https://github.com/timqi/SectorProgressView/raw/master/art/example.apk)
-
-1. CircularFillableLoaders
-一个可以用于闪屏或者其他地方加载的控件
-项目地址:https://github.com/lopspower/CircularFillableLoaders
-Demo地址:(https://play.google.com/store/apps/details?id=com.mikhaellopez.circularfillableloaderssample)
-
-1. PinWheel
-一个旋转的大风车Dialog
-项目地址:https://github.com/codingWang/PinWheel
-
-1. ColorArcProgressBar
-一个可定制的圆形进度条,通过xml参数配置可实现QQ健康中步数的弧形进度显示、仪盘表显示速度、最常见的下载进度条等功能。
-项目地址:https://github.com/Shinelw/ColorArcProgressBar
-
-### 八、TextView
-包括 TextView 及所有继承自 TextView 控件,如 EditText、Button、RadioButton
-
-1. android-flowtextview
-文字自动环绕其他 View 的 Layout
-项目地址:https://github.com/deano2390/FlowTextView
-
-1. Android Form EditText
-验证输入合法性的编辑框,支持输入、英文、ip、url 等多种正则验证
-项目地址:https://github.com/vekexasia/android-edittext-validator
-Demo 地址:https://play.google.com/store/apps/details?id=com.andreabaccega.edittextformexample
-
-1. Emojicon
-支持 emojis 的 TextView 和 EditText
-项目地址:https://github.com/rockerhieu/emojicon
-文档介绍:http://rockerhieu.com/emojicon/
-
-1. android-circlebutton
-Android 圆形按钮,实际实现是继承自 ImageView
-项目地址:https://github.com/markushi/android-circlebutton
-Demo 地址:https://github.com/markushi/android-circlebutton/blob/master/example/example.apk
-
-1. Segmented Radio Buttons for Android
-iOS’s segmented controls 的实现
-项目地址:https://github.com/vinc3m1/android-segmentedradiobutton
-Demo 地址:https://github.com/thquinn/DraggableGridView/blob/master/bin/DraggableGridViewSample.apk?raw=true
-
-1. Chips EditText Library
-支持国家名字联想从而选择显示该国国旗的 EditText,实际就是通过 SpannableStringBuilder 实现
-项目地址:https://github.com/kpbird/chips-edittext-library
-Demo 地址:https://github.com/kpbird/chips-edittext-library/tree/master/ChipsEditTextDemo/bin
-
-1. AutoFitTextView
-可固定边界内容字体大小自适应的 TextView
-项目地址:https://github.com/grantland/android-autofittextview
-
-1. Shimmer for Android
-文字发淡光的 TextView
-项目地址:https://github.com/RomainPiel/Shimmer-android
-
-1. Titanic
-可以显示水位上升下降(不知道该怎么描述 囧)的 TextView
-项目地址:https://github.com/RomainPiel/Titanic
-
-1. android-iconify
-提供带 Icon 的 TextView,Menu,Button 等
-项目地址:https://github.com/JoanZapata/android-iconify
-
-1. Calligraphy
-让我们在 android 开发中使用自定义字体变得更加简单
-项目地址:https://github.com/chrisjenx/Calligraphy
-
-1. CreditsRoll
-类似星球大战字幕效果的 TextView
-项目地址:https://github.com/frakbot/CreditsRoll
-
-1. android-process-buton
-带加载或提交进度的 Button
-项目地址:https://github.com/dmytrodanylyk/android-process-buton
-
-1. FButton
-扁平化的 Button
-项目地址:https://github.com/hoang8f/android-flat-button
-Demo 地址:https://play.google.com/store/apps/details?id=info.hoang8f.fbutton.demo
-
-1. FloatingActionButton
-一个类似 Android 版 Google+浮动功能按钮的控件,可以响应 ListView 的滚动事件。当列表向上滚动的时候会自动显示,向下滚动的时候自动隐藏。
-项目地址:https://github.com/makovkastar/FloatingActionButton
-Demo 地址:https://github.com/makovkastar/FloatingActionButton/tree/master/
-
-1. Android Saripaar
-Android 表单验证
-项目地址:https://github.com/ragunathjawahar/android-saripaar/
-
-1. JumpingBeans
-文字像 Mexican beans 一样跳动
-项目地址:https://github.com/frakbot/JumpingBeans
-Demo 地址:http://play.google.com/store/apps/details?id=net.frakbot.jumpingbeans.demo
-
-1. FancyButtons
-一个不用图片就可以帮助我们创建出漂亮按钮的库。
-项目地址:https://github.com/medyo/fancybuttons
-Demo 地址:https://github.com/medyo/fancybuttons/tree/master/samples
-
-1. Android-RobotoTextView
-一个实现了所有 Roboto 字体的 TextView,包括新出的 Roboto Slab 字体。
-项目地址:https://github.com/johnkil/Android-RobotoTextView
-Demo 地址:http://play.google.com/store/apps/details?id=com.devspark.robototextview
-Demo 项目:https://github.com/johnkil/Android-RobotoTextView/tree/master/robototextview-sample
-
-1. Android-WizardPager
-一个表单向导库
-项目地址:https://github.com/romannurik/android-wizardpager
-Demo 项目:https://github.com/str4d/android-wizardpager/tree/textfield
-
-1. RippleView
-一个实现了 Android L 上才引入的点击按钮后出现水波纹效果的按钮
-项目地址:https://github.com/siriscac/RippleView
-Demo 项目:https://github.com/siriscac/RippleView/tree/master/RippleViewExample
-
-1. RippleEffect
-一个实现 Material Design Ripple 效果的库,支持 Android API 9+以上版本。
-项目地址:https://github.com/traex/RippleEffect
-Demo 项目:https://github.com/traex/RippleEffect/tree/master/sample
-
-1. RippleCompat
-使用易于移植的方式为UI控件实现波纹效应,可以适配控件原背景及Palette支持。api简单,可自定义效果,兼容至Android API 7+。
-项目地址:https://github.com/desmond1121/RippleCompat
-Demo 项目:https://github.com/desmond1121/RippleCompat/tree/master/app
-
-1. Android Floating Label Widgets
-包含一系列控件,这些控件特点是:有默认值,当值不为空时默认值浮动到上面变为提示
-项目地址:https://github.com/marvinlabs/android-floatinglabel-widgets
-Demo 地址:https://play.google.com/store/apps/details?id=com.marvinlabs.widget.floatinglabel.demo
-在线演示:http://www.youtube.com/watch?v=hpZD9gJcRg0&feature=youtu.be
-
-1. MaterialEditText
-EditText 的 Material Design 实现。包含 [Google Material Design Spec](http://www.google.com/design/spec/components/text-fields.html)中的 UI 效果和一些新增特性。
-项目地址:https://github.com/rengwuxian/MaterialEditText
-
-1. MaterialEditText
-实现 [Material Design](http://www.google.com/design/spec/components/text-fields.html) 中 Ripple 效果线条背景的 EditText。直接替换原生 EditText 即可集成。
-项目地址:https://github.com/DreamingInCodeZH/MaterialEditText
-
-1. MultiActionTextView
-可以分别给 TextView 中的某几个字设置点击事件的 TextView
-项目地址:https://github.com/ajaysahani/MultiActionTextView
-
-1. ToggleButton
-状态切换的 Button,类似 iOS,用 View 实现
-项目地址:https://github.com/zcweng/ToggleButton
-
-1. SwitchButton
-状态切换的 Button,类似 iOS,拥有良好的用户界面
-项目地址:https://github.com/kyleduo/SwitchButton
-
-1. SlideSwitch
-状态切换的开关,可以设置为类似 IOS 的圆形,也可以设置为矩形,用 View 实现
-项目地址:https://github.com/Leaking/SlideSwitch
-
-1. ExpandableTextView
-可展开和收缩内容的 TextView。
-项目地址:https://github.com/Manabu-GT/ExpandableTextView
-
-1. TagCloudView(支持 SingleLine 模式的标签云)
-标签云效果,只需要一行代码即可设置为 SingleLine 模式,轻松实现知乎问题话题列表效果。SingleLine 模式时末尾文字及图片可自定义。
-项目地址:https://github.com/kingideayou/TagCloudView
-示例APK地址:https://github.com/kingideayou/TagCloudView/raw/master/apk/Demo.apk
-APP示例:知乎
-
-1. BubbleTextView
-Android Bubble View
-项目地址:https://github.com/dupengtao/BubbleTextView
-
-1. shadow-layout
-Android Shadow Layout
-解决了`CardView`:1.不能画圆的阴影。2.设置阴影的位置。3.设置阴影颜色。
-项目地址:https://github.com/dmytrodanylyk/shadow-layout
-
-### 九、ScrollView
-1. Discrollview
-支持滚动时 Item 淡入淡出,平移,缩放效果的 ScrollView
-项目地址:https://github.com/flavienlaurent/discrollview
-Demo 地址:https://github.com/flavienlaurent/discrollview/raw/master/sample.apk
-
-1. PullScrollView
-仿照新浪微博 Android 客户端个人中心的 ScrollView,下拉背景伸缩回弹效果。
-项目地址:https://github.com/MarkMjw/PullScrollView
-
-1. ArcLayout
-一个非常简单的弧布局库
-项目地址:https://github.com/ogaclejapan/ArcLayout
-Demo 地址:https://play.google.com/store/apps/details?id=com.ogaclejapan.arclayout.demo
-
-1. ParallaxScrollView
-支持视差滚动的 ScrollView ,背景图片的滚动速度小于 ScrollView 中子控件的滚动速度
-项目地址:https://github.com/chrisjenx/ParallaxScrollView
-Demo 地址:http://cloud.github.com/downloads/chrisjenx/ParallaxScrollView/ParallaxScrollViewDemo-v1.0.5.apk
-
-1. AKParallax-Android
-支持视差滚动的 ScrollView
-项目地址:https://github.com/ideaismobile/AKParallax-Android
-Demo 地址:https://play.google.com/store/apps/details?id=com.appkraft.parallax_sample
-
-1. Android-ObservableScrollView
-监听滚动视图滚动事件的库,帮助与 Toolbar 的交互动效处理与 Material Design 的实现
-项目地址:https://github.com/ksoichiro/Android-ObservableScrollView
-Demo 地址:https://play.google.com/store/apps/details?id=com.github.ksoichiro.android.observablescrollview.samples2
-
-1. OverScrollView
-有弹性的 ScrollView,实现了当手指滑动到 ScrollView 的顶部、底部时,可以继续的向上、向下拉伸。当释放手指的时候,向上、下回弹
-项目地址:https://github.com/EverythingMe/OverScrollView
-
-### 十、TimeView
-包括 TimePicker、DatePicker、CalendarView、Clock 等时间相关控件
-
-1. android-times-square
-Android 日历时间部件,支持选取单个日期,多个日期,及日期区间段和对话框形式显示
-项目地址:https://github.com/square/android-times-square
-Demo 地址:https://github.com/Trinea/TrineaDownload/blob/master/times-square-demo.apk?raw=true
-
-1. android-calendar-card
-日历
-项目地址:https://github.com/kenumir/android-calendar-card
-Demo 地址:https://play.google.com/store/apps/details?id=com.wt.calendarcardsample
-
-1. AndroidWheel
-Android Wheel 支持城市、多种日期时间、密码、图片
-项目地址:https://code.google.com/p/android-wheel/
-
-1. GoogleDateTimePickers
-时间选择部件
-项目地址:https://github.com/Mirkoddd/GoogleDateTimePickers
-文档介绍:https://play.google.com/store/apps/details?id=com.mirko.sample&hl=it
-
-1. DateTimePicker
-日期选择部件(Google Agenda 的样式风格)
-项目地址:https://github.com/flavienlaurent/datetimepicker
-Demo 地址:https://raw.github.com/biboune/datetimepicker/master/datetimepicker-sample.apk
-
-1. android-betterpickers
-提供日期、数字、时间(数字方式和钟表方式)、重复周期(闹钟的周期重复)、HMS(时、分、秒)的选择,支持以 DialogFragment 的弹窗选择
-项目地址:https://github.com/derekbrameyer/android-betterpickers
-Demo 地址:https://play.google.com/store/apps/details?id=com.doomonafireball.betterpickers.sample
-
-1. Android Week View
-日期控件,支持周,天视图,支持自定义样式
-项目地址:https://github.com/alamkanak/Android-Week-View
-
-1. PickerView
-仿 iOS 的 PickerView 控件,有时间选择和选项选择并支持一二三级联动效果,TimePopupWindow 时间选择器,支持年月日时分,年月日,时分等格式;OptionsPopupWindow 选项选择器,支持一,二,三级选项选择,并且可以设置是否联动
-项目地址:https://github.com/saiwu-bigkoo/Android-PickerView
-
-1. CountdownView
-Android 倒计时控件,使用Canvas绘制,支持多种样式
-项目地址:https://github.com/iwgang/CountdownView
-
-### 十一、TipView
-包括 Toast、角标、UndoBar 等提示性控件
-
-1. SVProgressHUD
-SVProgressHUD For Android 精仿iOS的提示库 SVProgressHUD,api也几乎一样。
-项目地址:https://github.com/saiwu-bigkoo/Android-SVProgressHUD
-
-1. Crouton
-丰富样式的 Toast,允许 alert、comfirm、info 样式及点击消失样式,允许设置 Toast 显示时间,允许自定义 View。 本文 32. SuperToasts 为其扩展版
-项目地址:https://github.com/keyboardsurfer/Crouton
-Demo 地址:http://play.google.com/store/apps/details?id=de.keyboardsurfer.app.demo.crouton
-
-1. supertooltips
-带动画效果的 Tips 显示
-项目地址:https://github.com/nhaarman/supertooltips
-Demo 地址:https://play.google.com/store/apps/details?id=com.haarman.supertooltips
-
-1. Android ViewBadger
-为其他 View 添加角标(消息圆点)等
-项目地址:https://github.com/jgilfelt/android-viewbadger
-Demo 地址:https://github.com/Trinea/TrineaDownload/blob/master/android-viewbadger.apk?raw=true
-
-1. SuperToasts
-更丰富样式的 toast,支持 Button、Progress、Horizontal Progress 样式、支持进入动画、支持撤销及其动画设置
-项目地址:https://github.com/JohnPersano/SuperToasts
-Demo 地址:https://play.google.com/store/apps/details?id=com.supertoastsdemo
-
-1. UndoBar
-屏幕底部显示取消或是确认的 PopupWindows
-项目地址:https://github.com/soarcn/UndoBar
-
-1. UndoBar
-屏幕底部显示取消或是确认某操作
-项目地址:https://github.com/jenzz/Android-UndoBar
-
-1. Android-ActionItemBadge
-可以在 ActionBar 的 MenuItem 上显示一个小角标
-项目地址:https://github.com/mikepenz/Android-ActionItemBadge
-
-1. SnackBar
-Material Design 风格的 Toast,类似 Google inbox 中的提示
-项目地址:https://github.com/MrEngineer13/SnackBar
-Demo 地址:https://play.google.com/store/apps/details?id=com.mrengineer13.snackbar.sample
-
-1. HeadsUp
-在 2.3 上使用 android 5.0 的 HeadsUp 效果
-项目地址:https://github.com/zzz40500/HeadsUp
-
-### 十二、FlipView
-1. android-flip
-类似 Flipboard 翻转动画的实现
-项目地址:https://github.com/openaphid/android-flip
-Demo 地址:https://github.com/openaphid/android-flip/blob/master/FlipView/Demo/APK/Aphid-FlipView-Demo.apk?raw=true
-APP 示例:flipboard
-
-1. FlipImageView
-支持 x、y、z 及动画选择的翻转动画的实现
-项目地址:https://github.com/castorflex/FlipImageView
-Demo 地址:https://play.google.com/store/apps/details?id=fr.castorflex.android.flipimageview
-
-1. FoldableLayout
-Flip 翻转效果的 ListView,目前还不支持 ListView 缓存
-项目地址:https://github.com/alexvasilkov/FoldableLayout
-Demo 地址:http://play.google.com/store/apps/details?id=com.alexvasilkov.foldablelayout.sample
-
-1. FlipViewPager.Draco
-Flip 翻转效果的 ViewPager
-项目地址:https://github.com/Yalantis/FlipViewPager.Draco
-
-### 十三、ColorPickView
-1. ColorPickerView
-颜色选择器,支持 PopupWindows 或新的 Activity 中打开
-项目地址:https://code.google.com/p/color-picker-view/
-
-1. HoloColorPicker
-颜色选择器
-项目地址:https://github.com/LarsWerkman/HoloColorPicker
-Demo 地址:https://docs.google.com/file/d/0BwclyDTlLrdXRzVnTGJvTlRfU2s/edit
-
-1. ColorPickerPreference
-颜色选择器
-项目地址:https://github.com/attenzione/android-ColorPickerPreference
-
-1. ColorPicker
-颜色选择器(Google Agenda 中的样式风格)
-项目地址:https://github.com/flavienlaurent/colorpicker
-Demo 地址:https://raw.github.com/biboune/colorpicker/master/colorpicker-sample.apk
-
-### 十四、GraphView
-1. MPAndroidChart
-强大的图表绘制工具,支持折线图、面积图、散点图、时间图、柱状图、条图、饼图、气泡图、圆环图、范围(高至低)条形图、网状图等;支持图的拖拽缩放;支持 Android 2.2 以上,支持横纵轴缩放,多指缩放,展现动画、高亮、保存到 sdcard、从文件读取图表
-项目地址:https://github.com/PhilJay/MPAndroidChart
-Demo 地址:https://play.google.com/store/apps/details?id=com.xxmassdeveloper.mpchartexample
-Demo 项目:https://github.com/PhilJay/MPAndroidChart/tree/master/MPChartExample
-在线演示:https://www.youtube.com/watch?v=ufaK_Hd6BpI
-
-1. achartengine
-强大的图表绘制工具,支持折线图、面积图、散点图、时间图、柱状图、条图、饼图、气泡图、圆环图、范围(高至低)条形图、拨号图/表、立方线图及各种图的结合
-项目地址:https://code.google.com/p/achartengine/
-官网网址:http://www.achartengine.org/
-APP 示例:Wordpress Android,Google Analytics
-
-1. GraphView
-绘制图表和曲线图的 View,可用于 Android 上的曲形图、柱状图、波浪图展示
-项目地址:https://github.com/jjoe64/GraphView
-Demo 项目:https://github.com/jjoe64/GraphView-Demos
-APP 示例:Wordpress Android,Google Analytics
-
-1. HoloGraphLibrary
-绘制现状图、柱状图、饼状图
-项目地址:https://bitbucket.org/danielnadeau/holographlibrary/src
-文档介绍:https://bitbucket.org/danielnadeau/holographlibrary/wiki/Home
-
-1. XCL-Charts
-XCL-Charts 基于原生的 Canvas 来绘制各种图表,在设计时,尽量在保证开发效率的同时,给使用者提供足够多的定制化能力。因此使用简便,同时具有相当灵活的定制能力。目前支持 3D/非 3D 柱形图(Bar Chart)、3D/非 3D 饼图(Pie Chart)、堆积图(Stacked Bar Chart)、面积图(Area Chart)、 折线图(Line Chart)、曲线图(Spline Chart)、环形图(Dount Chart)、南丁格尔玫瑰图(Rose Chart)、仪表盘(Dial Chart)、刻度盘(Gauge Chart)、雷达图(Radar Chart)、圆形图(Circle Chart)等图表。其它特性还包括支持图表缩放、手势移动、动画显示效果、高密度柱形显示、图表分界定制线、多图表的混合显示及同数据源不同类型图表切换等。
-项目地址:https://github.com/xcltapestry/XCL-Charts
-Demo 地址:https://github.com/xcltapestry/XCL-Charts/blob/master/XCL-Charts-demo/bin/XCL-Charts-demo.apk?raw=true
-
-1. EazeGraph
-Android 图表库,支持柱状图、分层柱状图、饼状图、线性图
-项目地址:https://github.com/blackfizz/EazeGraph
-Demo 地址:https://play.google.com/store/apps/details?id=org.eazegraph.app
-
-1. WilliamChart
-绘制图表的库,支持 LineChartView、BarChartView 和 StackBarChartView 三中图表类型,并且支持 Android 2.2 及以上的系统。
-项目地址:https://github.com/diogobernardino/WilliamChart
-Demo 地址:https://play.google.com/store/apps/details?id=com.db.williamchartdemo
-Demo 项目:https://github.com/diogobernardino/WilliamChart/tree/master/sample
-
-1. HelloCharts for Android
-支持折线图、柱状图、饼图、气泡图、组合图;支持预览、放大缩小,滚动,部分图表支持动画;支持 Android 2.2 以上
-项目地址:https://github.com/lecho/hellocharts-android
-Demo 地址:https://play.google.com/store/apps/details?id=lecho.lib.hellocharts.samples
-在线演示:https://www.youtube.com/watch?v=xbSBjyjH2SY
-
-1. PieChartView
-比较简单直接的饼状统计报表图,使用方便,设置相应的属性参数即可
-项目地址:https://github.com/wuseal/PieChartView
-Demo 地址:https://github.com/wuseal/PieChartView
-
-### 十五、UI Style
-不同样式的系统 UI 风格,如 IOS、Bootstrap 风格
-
-1. UITableView
-ios 风格控件,包括 Button、ListView、TableView
-项目地址:https://github.com/thiagolocatelli/android-uitableview
-Demo 地址:https://github.com/Trinea/TrineaDownload/blob/master/ui-tableview-demo.apk?raw=true
-
-1. ATableView
-ios 风格控件
-项目地址:https://github.com/dmacosta/ATableView
-Demo 地址:https://play.google.com/store/apps/details?id=com.nakardo.atableview.demo
-
-1. Cards-UI
-卡片式 View,支持单个卡片,item 为卡片的 ListView
-项目地址:https://github.com/afollestad/Cards-UI
-Demo 地址:https://github.com/Trinea/TrineaDownload/blob/master/cards-ui-demo.apk?raw=true
-
-1. cardslib
-卡片式 View,支持单个卡片,item 为卡片的 ListView 和 GridView
-项目地址:https://github.com/gabrielemariotti/cardslib
-Demo 地址:https://play.google.com/store/apps/details?id=it.gmariotti.cardslib.demo
-
-1. Android-Bootstrap
-Bootstrap 风格的按钮
-项目地址:https://github.com/Bearded-Hen/Android-Bootstrap
-
-1. Material Design Android Library
-Android L 中 Material Design 风格的组件适配到 Android 2.2+
-项目地址:https://github.com/navasmdc/MaterialDesignLibrary
-
-1. Android FlatUI
-Android 扁平化风格的组件,支持一些自定义样式
-项目地址:https://github.com/eluleci/FlatUI
-
-1. cheesesquare
-全新 Android 设计库演示 Demo,包括 Collapsing Toolbar、FloatingActionButton、View anchoring、NavigationView、Snackbar
-项目地址:https://github.com/chrisbanes/cheesesquare
-
-### 十六、其他
-1. SnappingStepper
-一种漂亮的UI控件,能更灵活的控制数字的增减。可用于购物车商品数量控制。
-项目地址:https://github.com/saiwu-bigkoo/Android-SnappingStepper
-
-1. SwipeBackLayout
-左右或向上滑动返回的 Activity
-项目地址:https://github.com/Issacw0ng/SwipeBackLayout
-Demo 地址:https://play.google.com/store/apps/details?id=me.imid.swipebacklayout.demo
-APP 示例:知乎
-
-1. android-styled-dialogs
-可自定义样式的 dialog,默认与 Holo 主题样式一致,在 Android2.2 以上同一样式
-项目地址:https://github.com/inmite/android-styled-dialogs
-Demo 地址:https://github.com/Trinea/TrineaDownload/blob/master/styled-dialogs-demo.apk?raw=true
-
-1. Android Sliding Up Panel
-可拖动的 View,能在当前 Activity 上扶起一个可拖动的 Panel
-项目地址:https://github.com/umano/AndroidSlidingUpPanel
-Demo 地址:https://play.google.com/store/apps/details?id=com.sothree.umano
-APP 示例:Google Music 精简播放栏
-
-1. TableFixHeaders
-第一列固定的 Table
-项目地址:https://github.com/InQBarna/TableFixHeaders
-Demo 地址:http://bit.ly/13buAIq
-
-1. Inscription
-可用于展示应用 change 和 new feature 信息
-项目地址:https://github.com/MartinvanZ/Inscription
-
-1. ActivityTransition
-Activity 切换动画,包括渐变、flip、某个位置进入等等
-项目地址:https://github.com/ophilbert/ActivityTransition
-文档介绍:https://github.com/jfeinstein10/JazzyViewPager/blob/master/JazzyViewPager.apk?raw=true
-
-1. EasyAndroidAnimations
-针对 View 的各种动画
-项目地址:https://github.com/2359media/EasyAndroidAnimations
-
-1. ViewAnimation
-对android view 动画进行封装,实现起更简单,自带三种常见运动路径,其他的可自定义
-项目地址:https://github.com/guohuanwen/ViewAniamtion
-APP 示例:QQ名片圈圈效果
-
-1. AndroidLoadingAnimation
-多种android加载动画
-项目地址:https://github.com/guohuanwen/AndroidLoadingAnimation
-
-1. android-lockpattern
-Android 的图案密码解锁
-项目地址:https://code.google.com/p/android-lockpattern/
-原理剖析文档:[android-lockpattern](http://a.codekk.com/detail/Android/爱早起/Android Lock Pattern 源码解析)
-Demo 地址:https://play.google.com/store/apps/details?id=group.pals.android.lib.ui.lockpattern.demo
-文档介绍:https://code.google.com/p/android-lockpattern/wiki/QuickUse
-APP 示例:Android 开机的图案密码解锁,支付宝的密码解锁
-
-1. PatternLock
-另一个 Android 图案解锁库
-项目地址:https://github.com/DreaminginCodeZH/PatternLock
-Demo 地址:https://github.com/DreaminginCodeZH/PatternLock/raw/master/dist/sample.apk
-APP 示例:Android 开机的图案密码解锁,支付宝的密码解锁
-
-1. RangeBar
-类似于 SeekBar,不同的是可以选择一个范围内的值而不是单个值
-项目地址:https://github.com/edmodo/range-bar
-Demo 地址:https://github.com/Trinea/TrineaDownload/blob/master/range-bar-demo.apk?raw=true
-
-1. ChromeView
-利用 Chromium 实现的 WebView,解决各个 Android 版本 WebView 不同的问题,同时利用最新 Chrome 代码
-项目地址:https://github.com/pwnall/chromeview
-
-1. android-phased-seek-bar
-支持预先定义状态的 SeekBar
-项目地址:https://github.com/ademar111190/android-phased-seek-bar
-
-1. Android Slider Preference Library
-可添加到设置中的基于对话框的 RankBar 小部件
-项目地址:https://github.com/jayschwa/AndroidSliderPreference
-
-1. ShowcaseView library
-用于高亮显示应用程序的特定部分,从而突出突出重点
-项目地址:https://github.com/amlcurran/ShowcaseView
-
-1. android-segmented-control
-Android 上的 Segmented Controls,相当于 RadioButton 组
-项目地址:https://github.com/hoang8f/android-segmented-control
-
-1. Swipeable Cards
-类似 Tinder 的卡片效果,可以加载图片并动画效果展示,向左滑动表示喜欢,向右表示不喜欢
-项目地址:https://github.com/kikoso/Swipeable-Cards
-Demo 地址:https://play.google.com/store/apps/details?id=info.hoang8f.fbutton.demo
-
-1. EdgeEffectOverride
-改变 ScrollView, ListView, ExpandableListView, GridView, ViewPager 等滚动控件滚动到边缘的颜色效果
-项目地址:https://github.com/AndroidAlliance/EdgeEffectOverride
-
-1. android-pinned-header-listviews
-使 ExpandListView 的 Group 滑动到顶端时会固定不动直到另外一个 Group 到达顶端
-项目地址:https://github.com/rtyley/android-pinned-header-listviews
-
-1. AndroidSwipeLayout
-滑动 Layout,支持单个 View,ListView,GridView
-项目地址:https://github.com/daimajia/AndroidSwipeLayout
-Demo 地址:[Download Demo](https://github.com/daimajia/AndroidSwipeLayout/releases/download/v1.0.0/AndroidSwipeLayout-Demo-1.0.1-snapshot.apk)
-
-1. DynamicCardLayout
-在 Android 中实现的类似 Windows8 的瓷片布局
-项目地址:https://github.com/dodola/DynamicCardLayout
-
-1. Emoticons-Keyboard
-带表情情符号的自定义键盘
-项目地址:https://github.com/chiragjain/Emoticons-Keyboard
-
-1. Android Typeface Helper
-可以帮你轻松实现自定义字体的库
-项目地址:https://github.com/norbsoft/android-typeface-helper
-
-1. Android-Anim-Playground
-几个动画效果,其中第二个基于 android-svg 的绘制效果非常好
-项目地址:https://github.com/Tibolte/Android-Anim-Playground
-
-1. AlertView
-仿iOS的AlertViewController 几乎完美还原iOS 的 AlertViewController ,同时支持Alert和ActionSheet模式,每一个细节都是精雕细琢,并把api封装成懒到极致模式,一行代码就可以进行弹窗.
-项目地址:https://github.com/saiwu-bigkoo/Android-AlertView
-
-1. NiftyDialogEffects
-支持自定义飞入动画样式的 Dialog
-项目地址:https://github.com/sd6352051/NiftyDialogEffects
-在线演示:http://tympanus.net/Development/ModalWindowEffects/
-
-1. PostOffice
-创建 Holo 及 Material Design 样式的 Dialog
-项目地址:https://github.com/r0adkll/PostOffice
-
-1. Swipecards
-类似 Tinder 的卡片效果,可以加载图片并动画效果展示,向左滑动表示喜欢,向右表示不喜欢,根据 Kikoso's Swipeable-Cards 改造而来
-项目地址:https://github.com/Diolor/Swipecards
-
-1. SeekArc
-圆形的 SeekBar
-项目地址:https://github.com/TriggerTrap/SeekArc
-
-1. BlurDialogFragment
-显示 DialogFragment 时背景模糊效果
-项目地址:https://github.com/tvbarthel/BlurDialogFragment
-Demo 地址:https://play.google.com/store/apps/details?id=fr.tvbarthel.lib.blurdialogfragment.sample
-
-1. range-seek-bar
-随机值选取的 SeekBar
-项目地址:https://github.com/yahoo/android-range-seek-bar
-
-1. MaterialRangeBar
-可以选择一个范围内的值而不是单个值的 SeekBar,RangeBar 的 Material Design 风格适配
-项目地址:https://github.com/oli107/material-range-bar
-
-1. MaterialList
-Material Design 风格的 CardView
-项目地址:https://github.com/dexafree/MaterialList
-Demo 地址:https://play.google.com/store/apps/details?id=com.dexafree.materiallistviewexample
-
-1. road-trip
-设置 path 的各种动画效果,以及如何实现复杂路径动画,类似于 ios 中的指纹注册界面的指纹动画效果
-项目地址:https://github.com/romainguy/road-trip
-
-1. dialogplus
-一个简单的 Android 对话框,支持不同的弹出模式
-项目地址:https://github.com/orhanobut/dialogplus
-
-1. FlowLayout
-一个简单的流式布局,用法类似 LinearLayout,但是能够让子元素根据宽度自动换行
-项目地址:[FLowLayout](https://github.com/LyndonChin/AndroidFlowLayout)
-
-1. CircleSeekbar
-一个环形Seekbar组件,支持叠加复合使用
-项目地址:[CircleSeekbar](https://github.com/feeeei/CircleSeekbar)
-
-1. FinestWebView
-美麗的和可定制的Android的活動,顯示在一個應用程序的網頁。
-项目地址: https://github.com/TheFinestArtist/FinestWebView-Android
-Sample 地址: https://play.google.com/store/apps/details?id=com.thefinestartist.finestwebview.sample
-
-1. YouTubePlayerActivity
-只需通過一個URL來在新的活動播放YouTube視頻。它支持屏幕方向,媒體音量控制等。
-项目地址: https://github.com/TheFinestArtist/YouTubePlayerActivity
-Sample 地址: https://play.google.com/store/apps/details?id=com.thefinestartist.ytpa.sample
-
-1. material-camera
-material风格的camera。能使camera使用起来更简单的库。
-项目地址:https://github.com/afollestad/material-camera
-
-1. material-dialogs
-各种material风格的dialog。
-漂亮的的、易于使用的和可定制的Dialog API,你能够使用Material designed风格的Dialog到API 8。
-项目地址:https://github.com/afollestad/material-dialogs
-Sample地址:https://github.com/afollestad/material-dialogs/blob/master/sample/sample.apk
-
-1. NotifyUtil
-最常见的通知样式库
-项目地址:https://github.com/wenmingvs/NotifyUtil
-demo地址:https://github.com/wenmingvs/NotifyUtil#notifydemo
-
-1. CanPhotos
-使用fresco选取多张图片并可预览图片
-项目地址:https://github.com/canyinghao/CanPhotos
-
-1. CanDialog
-仿照系统Dialog所写,继承于FrameLayout,添加一些动画,一些显示类型。
-项目地址:https://github.com/canyinghao/CanDialog
-
-## 第二部分 工具库
-主要包括那些不错的开发库,包括依赖注入框架、图片缓存、网络请求、数据库 ORM 建模、Android 公共库、Android 高版本向低版本兼容、多媒体相关及其他。
-
-### 一、依赖注入 DI
-通过依赖注入减少 View、服务、资源简化初始化,事件绑定等重复繁琐工作
-
-1. AndroidAnnotations(Code Diet)
-android 快速开发框架
-项目地址:https://github.com/excilys/androidannotations
-文档介绍:https://github.com/excilys/androidannotations/wiki
-官网网址:http://androidannotations.org/
-特点:(1) 依赖注入:包括 view,extras,系统服务,资源等等
-(2) 简单的线程模型,通过 annotation 表示方法运行在 ui 线程还是后台线程
-(3) 事件绑定:通过 annotation 表示 view 的响应事件,不用在写内部类
-(4) REST 客户端:定义客户端接口,自动生成 REST 请求的实现
-(5) 没有你想象的复杂:AndroidAnnotations 只是在在编译时生成相应子类
-(6) 不影响应用性能:仅 50kb,在编译时完成,不会对运行时有性能影响。
-其他:与 roboguice 的比较:roboguice 通过运行时读取 annotations 进行反射,所以可能影响应用性能,而 AndroidAnnotations 在编译时生成子类,所以对性能没有影响
-
-1. roboguice
-帮你处理了很多代码异常,利用 annotation 使得更少的代码完成项目
-项目地址:https://github.com/roboguice/roboguice
-文档介绍:https://github.com/roboguice/roboguice/wiki
-
-1. butterknife
-利用 annotation 帮你快速完成 View 的初始化,减少代码
-项目地址:https://github.com/JakeWharton/butterknife
-文档介绍:http://jakewharton.github.io/butterknife/
-
-1. Dagger
-依赖注入,适用于 Android 和 Java
-项目地址:https://github.com/square/dagger
-原理剖析文档:[Dagger](http://a.codekk.com/detail/Android/扔物线/Dagger 源码解析)
-文档介绍:http://square.github.io/dagger/
-
-1. AutoParcel
-注解自动生成Parcelable实现代码的库
-项目地址:https://github.com/frankiesardo/auto-parcel
-这篇文章详细介绍了它的使用方法:[AutoParcel](https://passy.svbtle.com/simpler-android-apis-with-autoparcel)
-作者认为使用AutoParcel的好处是,可以简化构造函数,方便IDE代码自动完成,方便测试,前向兼容,生成不可变的对象。
-文档介绍:https://github.com/frankiesardo/auto-parcel
-
-
-
-### 二、图片缓存
-1. Android-Universal-Image-Loader
-图片缓存,目前使用最广泛的图片缓存,支持主流图片缓存的绝大多数特性。
-项目地址:https://github.com/nostra13/Android-Universal-Image-Loader
-原理剖析文档:[Android-Universal-Image-Loader](http://a.codekk.com/detail/Android/huxian99/Android Universal Image Loader 源码分析)
-Demo 地址:https://github.com/Trinea/TrineaDownload/blob/master/universal-imageloader-demo.apk?raw=true
-文档介绍:http://www.intexsoft.com/blog/item/74-universal-image-loader-part-3.html
-
-1. picasso
-square 开源的图片缓存
-项目地址:https://github.com/square/picasso
-文档介绍:http://square.github.io/picasso/
-特点:(1)可以自动检测 adapter 的重用并取消之前的下载
-(2)图片变换
-(3)可以加载本地资源
-(4)可以设置占位资源
-(5)支持 debug 模式
-
-1. Cube ImageLoader
-阿里巴巴一淘使用的图片加载,综合了 Android-Universal-Image-Loader 和 square 等组件优点,简单易用,良好的中文文档支持
-项目地址:https://github.com/etao-open-source/cube-sdk
-Demo 地址:https://github.com/liaohuqiu/cube-sdk/raw/master/cube-sdk-sample.apk
-文档介绍:http://cube-sdk.liaohuqiu.net/
-
-1. fresco
-一款强大的图片缓存工具,由 Facebook 开发
-项目地址:https://github.com/facebook/fresco
-文档介绍:http://frescolib.org/
-特点:(1) 两个内存缓存加上磁盘缓存构成了三级缓存
-(2) 支持流式,可以类似网页上模糊渐进式显示图片
-(3) 对多帧动画图片支持更好,如 Gif、WebP
-(4) 更多样的显示,如圆角、进度条、点击重试、自定义对焦点
-(5) 更多样的加载,如支持 EXIF、全面支持 WebP
-(6) 支持 Android 2.3+
-
-1. Glide
-Glide 是一个 android 平台上的快速和高效的开源的多媒体资源管理库,提供 多媒体文件的压缩,内存和磁盘缓存, 资源池的接口。
-它可以最大性能地在 Android 设备上读取、解码、显示图片和视频。Glide 可以将远程的图片、视频、动画图片等缓存在设备本地便于提高用户浏览图片的流畅体验。
-项目地址:https://github.com/bumptech/glide
-特点:
-(1) GIF 动画的解码
-(2) 本地视频剧照的解码
-(3) 支持缩略图
-(4) Activity 生命周期的集成
-(5) 转码的支持
-(6) 动画的支持
-(7) OkHttp 和 Volley 的支持
-
-### 三、网络请求
-1. Volley
-Google 提供的网络通信库,使得网络请求更简单、更快速
-项目地址:https://android.googlesource.com/platform/frameworks/volley
-文档介绍:http://commondatastorage.googleapis.com/io-2013/presentations/110%20-%20Volley-%20Easy,%20Fast%20Networking%20for%20Android.pdf
-
-1. Asynchronous Http Client for Android
-Android 异步 Http 请求
-项目地址:https://github.com/loopj/android-async-http
-文档介绍:http://loopj.com/android-async-http/
-特点:(1) 在匿名回调中处理请求结果
-(2) 在 UI 线程外进行 http 请求
-(3) 文件断点上传
-(4) 智能重试
-(5) 默认 gzip 压缩
-(6) 支持解析成 Json 格式
-(7) 可将 Cookies 持久化到 SharedPreferences
-
-1. android-query
-异步加载,更少代码完成 Android 加载
-项目地址:https://github.com/androidquery/androidquery 或 https://code.google.com/p/android-query/
-Demo 地址:https://play.google.com/store/apps/details?id=com.androidquery
-文档介绍:https://code.google.com/p/android-query/#Why_AQuery?
-特点:https://code.google.com/p/android-query/#Why_AQuery?
-
-1. Async Http Client
-Java 异步 Http 请求
-项目地址:https://github.com/AsyncHttpClient/async-http-client
-文档介绍:http://sonatype.github.io/async-http-client/
-
-1. Ion
-支持图片、json、http post 等异步请求
-项目地址:https://github.com/koush/ion
-文档介绍:https://github.com/koush/ion#more-examples
-
-1. Http Request
-项目地址:https://github.com/kevinsawicki/http-request
-文档介绍:https://github.com/kevinsawicki/http-request#examples
-
-1. okhttp
-square 开源的 http 工具类
-项目地址:https://github.com/square/okhttp
-文档介绍:http://square.github.io/okhttp/
-特点:(1) 支持 SPDY( http://zh.wikipedia.org/wiki/SPDY )协议。SPDY 协议是 Google 开发的基于传输控制协议的应用层协议,通过压缩,多路复用(一个 TCP 链接传送网页和图片等资源)和优先级来缩短加载时间。
-(2) 如果 SPDY 不可用,利用连接池减少请求延迟
-(3) Gzip 压缩
-(4) Response 缓存减少不必要的请求
-
-1. Retrofit
-RESTFUL API 设计
-项目地址:https://github.com/square/retrofit
-文档介绍:http://square.github.io/retrofit/
-
-1. RoboSpice
-Android 异步网络请求工具,支持缓存、REST 等等
-项目地址:https://github.com/stephanenicolas/robospice
-Demo 地址:https://github.com/stephanenicolas/RoboDemo/downloads
-
-1. TwistVolley
-TwistVolley 是对 Volley 的一个封装库。提供类似 Picasso 一样的串联式 API。使得使用 Volley 更加方便。
-项目地址:https://github.com/TwistedEquations/TwistVolley
-
-### 四、数据库 orm 工具包
-orm 的 db 工具类,简化建表、查询、更新、插入、事务、索引的操作
-
-1. greenDAO
-Android Sqlite orm 的 db 工具类
-项目地址:https://github.com/greenrobot/greenDAO
-文档介绍:http://greendao-orm.com/documentation/
-官网网址:http://greendao-orm.com/
-特点:(1) 性能佳
-(2) 简单易用的 API
-(3) 内存小好小
-(4) 库大小小
-
-1. GreenDaoUpgradeHelper
-greenDAO的数据库升级帮助类,只需一行代码解决数据库升级
-项目地址:https://github.com/yuweiguocn/GreenDaoUpgradeHelper
-
-1. ActiveAndroid
-Android Sqlite orm 的 db 工具类
-项目地址:https://github.com/pardom/ActiveAndroid
-文档介绍:https://github.com/pardom/ActiveAndroid/wiki/_pages
-
-1. Sprinkles
-Android Sqlite orm 的 db 工具类,比较显著的特点就是配合 https://github.com/square/retrofit 能保存从服务器获取的数据
-项目地址:https://github.com/emilsjolander/sprinkles
-文档介绍:http://emilsjolander.github.io/blog/2013/12/18/android-with-sprinkles/
-
-1. Realm
-移动端的数据库,适用于 Phone、Tablet、Wearable,支持 ORM,线程安全、支持连表及数据库加密,比 SQLite 性能更好
-项目地址:https://github.com/realm/realm-java
-文档介绍:http://realm.io/docs/java/0.72.0/
-
-1. ormlite-android
-项目地址:https://github.com/j256/ormlite-android
-文档介绍:http://ormlite.com/sqlite_java_android_orm.shtml
-
-1. Schematic
-根据 SQLite 生成 ContentProvider
-项目地址:https://github.com/SimonVT/schematic
-
-1. DBFlow
-Android SQLite ORM 工具库。综合了 Active Android, Schematic, Ollie,Sprinkles 等库的优点;通过注解实现,性能好;能生成 ContentProvider。
-项目地址:https://github.com/Raizlabs/DBFlow
-文档介绍:https://github.com/Raizlabs/DBFlow#usage-docs
-
-1. SnappyDB
-SnappyDB是一个key-value数据库,非常流行的NoSQL数据库。
-项目地址:https://github.com/nhachicha/SnappyDB
-演示Demo:https://play.google.com/store/apps/details?id=com.snappydb.snippets.app
-
-### 五、Android 公共库
-1. Guava
-Google 的基于 java1.6 的类库集合的扩展项目,包括 collections, caching, primitives support, concurrency libraries, common annotations, string processing, I/O 等等. 这些高质量的 API 可以使你的 JAVa 代码更加优雅,更加简洁
-项目地址:https://code.google.com/p/guava-libraries/
-文档介绍:https://code.google.com/p/guava-libraries/wiki/GuavaExplained
-
-1. AndroidCommon
-Android 公共库
-项目地址:https://github.com/Trinea/AndroidCommon
-Demo 地址:https://play.google.com/store/apps/details?id=cn.trinea.android.demo
-文档介绍:http://www.trinea.cn/android/android-common-lib/
-包括:a. ImageCache 图片缓存,包含内存和 Sdcard 缓存
-http://www.trinea.cn/android/android-imagecache/
-特点:(1)支持预取新图片,支持等待队列
-(2)包含二级缓存,可自定义文件名保存规则
-(3)可选择多种缓存算法(FIFO、LIFO、LRU、MRU、LFU、MFU 等 13 种)或自定义缓存算法
-(4)可方便的保存及初始化恢复数据
-(5)支持不同类型网络处理
-(6)可根据系统配置初始化缓存等
-
-1. HttpCache Http 缓存
-文档介绍:http://www.trinea.cn/android/android-http-cache
-特点是:(1) 根据 cache-control、expires 缓存 http 请求
-(2) 支持同步、异步 Http 请求
-(3) 在匿名回调中处理请求结果
-(4) 在 UI 线程外进行 http 请求
-(5) 默认 gzip 压缩
-(2) 公共 View(下拉及底部加载更多 ListView、底部加载更多 ScrollView、滑动一页 Gallery)
-(3) Android 常用工具类(网络、下载、Android 资源操作、shell、文件、Json、随机数、Collection 等等)
-
-1. DropDownListView 下拉刷新及滑动到底部加载更多 ListView
-文档介绍:http://www.trinea.cn/android/dropdown-to-refresh-and-bottom-load-more-listview/
-
-1. shipfaster
-整合了 Dagger Otto Retrofit Robolectric Picasso OkHttp,方便快速开发
-项目地址:https://github.com/pyricau/shipfaster
-
-1. CleanAndroidCode
-整合了 Dagger Otto AndroidAnnotations,方便快速开发
-项目地址:https://github.com/pyricau/CleanAndroidCode
-
-1. xUtils
-基于 Afinal,包含 DbUtils、ViewUtils、HttpUtils、BitmapUtils 四大模块,可用于快速开发
-项目地址:https://github.com/wyouflf/xUtils
-原理剖析文档:[xUtils](http://a.codekk.com/detail/Android/Caij/xUtils 源码解析)
-
-1. Afinal
-Afinal 是一个 android 的 ioc,orm 框架,内置了四大模块功能:FinalAcitivity,FinalBitmap,FinalDb,FinalHttp。通过 finalActivity,我们可以通过注解的方式进行绑定 ui 和事件。通过 finalBitmap,我们可以方便的加载 bitmap 图片,而无需考虑 oom 等问题。通过 finalDB 模块,我们一行代码就可以对 android 的 sqlite 数据库进行增删改查。通过 FinalHttp 模块,我们可以以 ajax 形式请求 http 数据
-项目地址:https://github.com/yangfuhai/afinal
-官网网址:http://www.afinal.org
-
-1. UltimateAndroid
-UltimateAndroid 是一个快速开发 Android 应用的框架,框架目前主要包含的功能有 View Injection,ORM,异步网络请求和图片加载,自动化脚本测试,磁盘 LRU 等功能.同时提供了类似于 TripleDes、Webview 快速设置、Md5 处理、String 处理,Https 处理等常用工具类,还有超过 100 多种 UI 控件效果。
-项目地址:https://github.com/cymcsg/UltimateAndroid
-官网网址:http://blog.marshalchen.com/UltimateAndroid/
-
-1. SAF
-SAF(Simple Android Framework)是一个简单的 android 框架,它为开发 Android app 提供了基础性组件。
-项目地址:https://github.com/fengzhizi715/SAF
-官网网址:http://www.salesuite.cn/
-包括:(1)Event Bus(事件总线)
-(2) Rest Client(http 的框架)
-(3) Image Cache(图片缓存)
-(4) Dependency Injection(依赖注入)
-(5) Sqlite ORM(sqlite 的 orm)
-(6) Router(Activity、Fragment 的 Router)
-(7) Utils(各种常用的工具类)
-
-1. Barber
-Custom View 神器。通过简单的注解帮助你大大减少 Custom View 中的代码量。由于和 ButterKnife 一样使用了 Annotation Proccessor ,所以对程序性能没有影响。
-项目地址:https://github.com/hzsweers/barber
-
-1. device-year-class
-A library that analyzes an Android device's specifications and calculates which year the device would be considered "high end”.(facebook 开发的检测手机主流配置工具)
-项目地址:https://github.com/facebook/device-year-class
-
-1. RxAndroid
-对RxJava在Android上的扩展,使得Android上也很容易实现reactive components,简化线程操作,从而写出很漂亮的代码。
-项目地址:https://github.com/ReactiveX/RxAndroid
-文档介绍:[给Android 开发者的 RxJava 详解](http://gank.io/post/560e15be2dca930e00da1083)
-
-1. MultiImageSelector
-一个本地图片选择器,支持图片的单选和多选,直接返回所选图片的路径。
-项目地址:https://github.com/lovetuzitong/MultiImageSelector.git
-
-### 六、Android 高版本向低版本兼容
-1. Nine Old Androids
-将 Android 3.0(Honeycomb)所有动画 API(ObjectAnimator ValueAnimator 等)兼容到 Android1.0
-项目地址:https://github.com/JakeWharton/NineOldAndroids
-原理剖析文档:[Nine Old Androids](http://a.codekk.com/detail/Android/Mr.Simple/NineOldAnimations 源码解析)
-Demo 地址:https://play.google.com/store/apps/details?id=com.jakewharton.nineoldandroids.sample
-文档介绍:http://nineoldandroids.com/
-
-1. HoloEverywhere
-将 Android 3.0 的 Holo 主题兼容到 Android2.1++
-项目地址:https://github.com/Prototik/HoloEverywhere
-Demo 地址:https://raw.github.com/Prototik/HoloEverywhere/repo/org/holoeverywhere/demo/2.1.0/demo-2.1.0.apk
-文档介绍:http://android-developers.blogspot.com/2012/01/holo-everywhere.html
-
-1. SherlockNavigationDrawer
-将 Android NavigationDrawer 和 ActionbarSherlock 结合,解决 4.0 以下 NavigationDrawer 的适配问题
-项目地址:https://github.com/tobykurien/SherlockNavigationDrawer
-文档介绍:http://developer.android.com/training/implementing-navigation/nav-drawer.html
-
-1. Notifications4EveryWhere
-将 Android 4.1 的 Notification 兼容到 Android2.2++
-项目地址:https://github.com/youxiachai/Notifications4EveryWhere
-
-1. Android Switch Widget Backport
-将 Android Switch 和 SwitchPreference 的兼容到 Android2.1++
-项目地址:https://github.com/BoD/android-switch-backport
-Demo 地址:https://play.google.com/store/apps/details?id=org.jraf.android.backport.switchwidget.sample
-文档介绍:https://github.com/BoD/android-switch-backport#using-the-switch
-
-1. android-datepicker
-将 Android 4.0 的 datepicker 兼容到 Android2.2++
-项目地址:https://github.com/SimonVT/android-datepicker
-
-1. Transitions Everywhere
-Android 4.4 的 Transitions API 兼容到 Android 2.2 以上
-项目地址:https://github.com/andkulikov/transitions-everywhere
-
-1. KShareViewActivityManager
-一个兼容Android 5.0 以下Shared Element Transition (页面间共享元素位移动画)库
-项目地址:https://github.com/kot32go/KShareViewActivityManager
-
-### 七、多媒体相关
-1. cocos2d-x
-跨平台的 2d 游戏框架,支持 Android、IOS、Linux、Windows 等众多平台
-项目地址:https://github.com/cocos2d/cocos2d-x
-文档介绍:http://www.cocos2d-x.org/wiki
-官网网址:http://www.cocos2d-x.org/
-
-1. JustWeEngine
-Android的轻量级游戏框架,能大大缩减编写原生游戏的代码量和难度,仍在持续更新中。
-项目地址: https://github.com/lfkdsk/JustWeEngine
-Demo地址: https://github.com/lfkdsk/EngineDemo
-
-1. Vitamio
-是一款 Android 与 iOS 平台上的全能多媒体开发框架
-项目地址:https://github.com/yixia/VitamioBundle
-网站介绍:http://www.vitamio.org/docs/
-特点:(1) 全面支持硬件解码与 GPU 渲染
-(2) 能够流畅播放 720P 甚至 1080P 高清 MKV,FLV,MP4,MOV,TS,RMVB 等常见格式的视频
-(3) 在 Android 与 iOS 上跨平台支持 MMS, RTSP, RTMP, HLS(m3u8)等常见的多种视频流媒体协议,包括点播与直播。
-
-1. VDPlayerSDK
-最容易集成的开源播放器组件,支持直播,支持软硬解切换,支持清晰度切换
-项目地址:https://github.com/SinaVDDeveloper/
-Demo 地址:https://github.com/SinaVDDeveloper/demo
-文档介绍:https://github.com/SinaVDDeveloper/sinavideo_playersdk
-特点:
-(1) 使用自定义控件方式提供播放器UI,不需要视频相关知识,能编写android应用就能使用
-(2) 软硬解切换、点播直播支持
-(3) 解码内核小于4M,更方便集成到非专业视频app中
-(4) 修正一些vitaimio的问题,比如m3u8可以拖动等等
-
-1. VideoPlayerManager
-帮助实现VideoPlayer控制,使得它更容易使用ListView和recyclerview。它还可以跟踪滚动列表中可见的项。当列表中的新项可见域最大时,该库给出了接口来跟踪它。
-项目地址:https://github.com/danylovolokh/VideoPlayerManager
-文档介绍:https://medium.com/@v.danylo/implementing-video-playback-in-a-scrolled-list-listview-recyclerview-d04bc2148429#.hs5n0geqn
-
-1. UniversalVideoView
-Android上定义播放器控件,支持自定义控制UI, 全屏播放, 重力感应自动切换横竖屏.
-项目地址:https://github.com/linsea/UniversalVideoView
-
-1. PhotoProcessing
-利用 ndk 处理图片库,支持 Instafix、Ansel、Testino、XPro、Retro、BW、Sepia、Cyano、Georgia、Sahara、HDR、Rotate(旋转)、Flip(翻转)等各种特效
-项目地址:https://github.com/lightbox/PhotoProcessing
-Demo 地址:https://github.com/Trinea/TrineaDownload/blob/master/photo-processing.apk?raw=true
-
-1. Android StackBlur
-图片模糊效果工具类
-项目地址:https://github.com/kikoso/android-stackblur
-Demo 地址:https://github.com/kikoso/android-stackblur/blob/master/StackBlurDemo/bin/StackBlurDemo.apk?raw=true
-文档介绍:https://github.com/kikoso/android-stackblur#usage
-
-1. Bitmap Smart Clipping using OpenCV
-图片智能裁剪保留重要部分显示
-项目地址:https://github.com/beartung/tclip-android
-利用淘宝的 http://code.taobao.org/p/tclip/ 库完成
-一淘玩客正在使用的图片裁剪,自动识别图片中的重要区域,并且在图片裁剪时保留重要区域
-特点:(1). 能进行人脸识别。图片中有人脸,将自动视为人脸区域为重要区域,将不会被裁剪掉
-(2).自动其它重要区域。如果图片中未识别出人脸,则会根据特征分布计算出重区域
-
-1. Cropper
-图片局部剪切工具,可触摸控制选择区域或旋转
-项目地址:https://github.com/edmodo/cropper
-文档介绍:https://github.com/edmodo/cropper/wiki
-
-1. uCrop
-Yalantis 出品的强大的图片裁剪库 ,支持缩放,旋转图片,支持各种比例的裁剪框
-项目地址:https://github.com/Yalantis/uCrop
-
-1. android-crop
-图片裁剪 Activity
-项目地址:https://github.com/jdamcd/android-crop
-
-1. TileView
-可分块显示大图,支持 2D 拖动、双击、双指放大、双指捏合
-项目地址:https://github.com/moagrius/TileView
-Demo 地址:http://moagrius.github.io/TileView/TileViewDemo.apk
-
-1. BlurEffectForAndroidDesign
-图片模糊效果
-项目地址:https://github.com/PomepuyN/BlurEffectForAndroidDesign
-
-1. android-eye
-PC 端网页查看同一局域网内的手机摄像头内容,可以用来监控哦
-项目地址:https://github.com/Teaonly/android-eye
-Demo 地址:https://play.google.com/store/apps/details?id=teaonly.droideye
-
-1. libpng for Android
-PNG 图片的 jni 库,支持几乎 png 的所有特性
-项目地址:https://github.com/julienr/libpng-android
-文档介绍:http://www.libpng.org/pub/png/libpng.html
-
-1. android-gpuimage
-基于 GPU 的图片滤镜
-项目地址:https://github.com/CyberAgent/android-gpuimage
-
-1. AndroidFaceCropper
-图片脸部自动识别,将识别后的局部图片返回
-项目地址:https://github.com/lafosca/AndroidFaceCropper
-
-1. Android Video Crop
-利用 TextureView 播放和剪切视频,类似 ImageView.setScaleType
-项目地址:https://github.com/dmytrodanylyk/android-video-crop
-Demo 地址:https://github.com/lafosca/AndroidFaceCropper/releases/download/1.0/FaceCropper-sample-debug-unaligned.apk
-
-1. svg-android
-Android Svg 矢量图形支持
-项目地址:https://github.com/japgolly/svg-android https://github.com/japgolly/svg-android
-
-1. Android Visualizer
-从 Android MediaPlayer 获得音频,然后像 iTunes 及 WinAmp 一样展示音轨
-项目地址:https://github.com/felixpalmer/android-visualizer
-
-1. ExoPlayer
-包括仪表板和 SmoothStreaming 自适应回放,缓存持久化和自定义渲染器,方便自定义和扩展,并且可以通过应用商店更新
-项目地址:https://github.com/google/ExoPlayer
-
-1. DanmakuFlameMaster
-android 上开源弹幕解析绘制引擎项目
-项目地址:https://github.com/ctiao/DanmakuFlameMaster
-
-1. OpenDanmaku
-android 上一个轻量级的更简单的开源弹幕控件
-项目地址:https://github.com/linsea/OpenDanmaku
-
-### 八、事件总线(订阅者模式)
-通过发布/订阅事件解耦事件发送和接受,从而简化应用程序组件(Activities, Fragments 及后台线程)之间的通信
-
-1. EventBus
-greenrobot 的开源项目
-项目地址:https://github.com/greenrobot/EventBus
-原理剖析文档:[EventBus](http://a.codekk.com/detail/Android/Trinea/EventBus 源码解析)
-文档介绍:https://github.com/greenrobot/EventBus#general-usage-and-api
-特点:(1) 支持在不同类型的线程中处理订阅,包括发布所在线程,UI 线程、单一后台线程、异步线程
-(2) 支持事件优先级定义,支持优先级高的订阅者取消事件继续传递,支持粘性事件,是不是跟系统的有序广播、粘性广播很像啊
-(3) 不是基于 annotations
-(4) 性能更优
-(5) 体积小
-(6) 支持单例创建或创建多个对象
-(7) 支持根据事件类型订阅
-
-1. Otto
-Square 的开源项目,基于 Guava 的 Android 优化
-项目地址:https://github.com/square/otto
-文档介绍:http://square.github.io/otto/
-[EventBus 与 Otto 的功能及性能对比文档](https://github.com/greenrobot/EventBus#comparison-with-squares-otto)
-[EventBus 与 Otto 性能对比 Demo Apk](https://play.google.com/store/apps/details?id=de.greenrobot.eventperf)
-
-### 九、传感器
-1. Great Android Sensing Toolkit
-Android 感应器工具包,包含示例及使用过程中可能需要的算法
-项目地址:https://github.com/gast-lib/gast-lib
-Demo 地址:https://play.google.com/store/apps/details?id=root.gast.playground
-文档介绍:https://github.com/gast-lib/gast-lib#documentation
-
-1. SensorManager
-Android 传感器管理
-项目地址:https://github.com/nlathia/SensorManager
-文档介绍:https://docs.google.com/document/d/1TqThJULb-4e6TGb1gdkAaPCfyuXStjJpbnt7a0OZ9OE/edit
-
-1. GPSLogger
-记录 GPS 信息
-项目地址:https://github.com/mendhak/gpslogger
-Demo 地址:https://play.google.com/store/apps/details?id=com.mendhak.gpslogger
-文档介绍:http://code.mendhak.com/gpslogger/
-
-1. Pedometer
-计步器,使用硬件计步感应器
-项目地址:https://github.com/j4velin/Pedometer
-
-1. leapcast
-ChromeCast 模拟器的 App
-项目地址:https://github.com/dz0ny/leapcast
-
-1. Arduino-Communicator
-与 Arduino 通信的 App
-项目地址:https://github.com/jeppsson/Arduino-Communicator
-
-1. android-pedometer
-Android 计步器
-项目地址:https://github.com/bagilevi/android-pedometer
-Demo 地址:http://pedometer.googlecode.com/files/Pedometer-1.4.apk
-
-1. OwnTracks for Android
-自己的轨迹记录
-项目地址:https://github.com/owntracks/android
-
-1. Shake Detector library for Android
-Android 手机震动摇晃检测库,提供供 UI 线程调用的回调接口
-项目地址:https://github.com/tbouron/ShakeDetector
-Demo 地址:https://play.google.com/store/apps/details?id=com.github.tbouron.shakedetector.example
-
-1. Android heart rate monitor
-Android 心跳检测
-项目地址:https://github.com/phishman3579/android-heart-rate-monitor
-
-1. Bluetooth LE Library for Android
-蓝牙源信息,包括宝库 Mac、更新时间、RSSI、UUID、信号源距离、影响范围等信息
-项目地址:https://github.com/alt236/Bluetooth-LE-Library---Android
-Demo 地址:https://play.google.com/store/apps/details?id=uk.co.alt236.btlescan
-
-1. farebot
-通过 NFC 从公交卡中读取数据的一个应用
-项目地址:https://github.com/codebutler/farebot
-
-### 十、安全
-1. SQLCipher
-Sqlite 加密工具
-项目地址:https://github.com/sqlcipher/sqlcipher
-文档介绍:http://sqlcipher.net/sqlcipher-for-android/
-
-1. Conceal
-快速高效的进行文件加密解密
-项目地址:https://github.com/facebook/conceal
-文档介绍:https://github.com/facebook/conceal#usage
-
-1. Android-PasscodeLock
-应用锁,每次启动或从任何 Activity 启动应用都需要输入四位数字的密码方可进入
-项目地址:https://github.com/wordpress-mobile/Android-PasscodeLock
-Demo 地址:https://play.google.com/store/apps/details?id=org.wordpress.android
-APP 示例:Wordpress Android,支付宝,挖财
-
-1. GlowPadBackport
-将 Android4.2 的锁屏界面解锁扩展到 Android1.6 及 1.6+
-项目地址:https://github.com/rock3r/GlowPadBackport
-Demo 地址:https://play.google.com/store/apps/details?id=net.sebastianopoggi.samples.ui.GlowPadSample
-
-1. GlowPadView
-Android 4 锁屏界面解锁
-项目地址:https://github.com/nadavfima/GlowPadView
-
-1. Android-InsecureBank
-关于 Android 不安全性的示例
-项目地址:https://github.com/dineshshetty/Android-InsecureBankv2
-
-### 十一、插件化
-更多见:[Android 插件化作用、概念以及不错的资料(包括开源项目)和解决方案](http://www.trinea.cn/android/android-plugin/)
-
-1. dynamic-load-apk
-Android 动态加载 Apk,热部署,利用 ClassLoader 以及 Activity 代理的方式解决
-项目地址:https://github.com/singwhatiwanna/dynamic-load-apk
-原理剖析文档:[dynamic-load-apk](http://a.codekk.com/detail/Android/FFish/DynamicLoadApk 源码解析)
-文档介绍:http://blog.csdn.net/singwhatiwanna/article/details/22597587
-
-1. Android Dynamic Loader
-点评的实现方式,和上面不同的是:他不是用代理 Activity 的方式实现而是用 Fragment 以及 Schema 的方式实现
-项目地址:https://github.com/mmin18/AndroidDynamicLoader
-Demo 地址:https://github.com/mmin18/AndroidDynamicLoader/raw/master/host.apk
-
-1. xCombine
-Android App 插件式插件开发,插件必须先安装,更推荐看上面两个开源项目
-项目地址:https://github.com/wyouflf/xCombine
-文档介绍:http://my.oschina.net/u/1171837/blog/155377
-
-1. Android Plugin Framework
-Android 插件式开发,开放的源码目前不完整
-项目地址:https://github.com/umeng/apf
-
-1. Android OpenAtlas
-Android插件框架,基于OSGI,非代理方式,组件需要在宿主中声明
-项目地址:https://github.com/bunnyblue/OpenAtlas
-Demo 地址:https://github.com/bunnyblue/OpenAtlasExtension/blob/master/Dist/OpenAtlasLauncher.apk
-
-1. multidex
-安装多 dex 的 classloader
-项目地址:https://github.com/casidiablo/multidex
-
-1. ANR-WatchDog
-Android ANR 监听,通过监听自己的 UI Thread 是否被执行确定是否发生了 ANR,并可以设置相关事件
-项目地址:https://github.com/SalomonBrys/ANR-WatchDog
-
-### 十二、文件
-对不同文档类型的处理,包括 PDF、Word、EPub、Html、Zip 等
-
-1. purePDF
-允许从任何运行的 SWF 文件读取和创建 PDF 文档
-项目地址:https://github.com/sephiroth74/purePDF
-
-1. android-pdfview
-快速解析 pdf 的 view,默认支持手势缩放和相关动画
-项目地址:https://github.com/JoanZapata/android-pdfview
-
-1. Office 365 SDK for Android Preview
-可支持 Microsoft SharePoint Lists, Microsoft SharePoint Files, Microsoft Exchange Calendar, Microsoft Exchange Contacts, Microsoft Exchange Mail
-项目地址:https://github.com/OfficeDev/Office-365-SDK-for-Android
-
-1. OpenSpritz-Android
-EPub 阅读器
-项目地址:https://github.com/OnlyInAmerica/OpenSpritz-Android
-
-1. jsoup
-一个解析 html 的 java 库,可方便的提取和操作数据
-项目地址:https://github.com/jhy/jsoup
-官网网址:http://jsoup.org/
-作用:(1) 从一个 url、文件或 string 获得 html 并解析
-(2) 利用 dom 遍历或 css 选择器查找、提取数据
-(3) 操作 html 元素
-(4) 根据白名单去除用于提交的非法数据防止 xss 攻击
-(5) 输出整齐的 html
-
-1. ZIP
-java 压缩和解压库
-项目地址:https://github.com/zeroturnaround/zt-zip
-文档介绍:https://github.com/zeroturnaround/zt-zip#examples
-作用:(1) 解压和压缩,并支持文件夹内递归操作
-(2) 支持包含和排除某些元素
-(3) 支持重命名元素
-(4) 支持遍历 zip 包内容
-(5) 比较两个 zip 包等功能
-
-1. Image File Selector
-轻量级的图片文件选择器,用系统api选取,压缩和裁切图片,可以方便的得要指定尺寸的图片
-项目地址:https://github.com/sw926/ImageFileSelector
-
-### 十三、其他
-
-1. FragmentStack
-一个封装了启动模式的Fragment便捷使用库,方便构建单Activity+多Fragment轻量级框架
-项目地址:https://github.com/Mr-wangyong/FragmentStack
-App示例:[南瓜电影](http://www.wandoujia.com/apps/cn.vcinema.cinema)
-
-1. CustomTabsHelper
-快速集成 CustomTabs 的工具类
-项目地址:https://github.com/DreaminginCodeZH/CustomTabsHelper
-
-1. Salvage view
-带 View 缓存的 Viewpager PagerAdapter,很方便使用
-项目地址:https://github.com/JakeWharton/salvage
-
-1. Android Priority Job Queue
-Android 后台任务队列
-项目地址:https://github.com/path/android-priority-jobqueue
-文档介绍:https://github.com/path/android-priority-jobqueue#getting-started
-
-1. Cobub Razor
-开源的 mobile 行为分析系统,包括 web 端、android 端,支持 ios 和 window phone
-项目地址:https://github.com/cobub/razor
-Demo 地址:http://demo.cobub.com/razor
-官网网址:http://dev.cobub.com/
-
-1. Countly
-Android 移动端数据采集分析系统
-项目地址:https://github.com/Countly/countly-sdk-android
-官网网址:https://count.ly/
-
-1. aFileChooser
-文件选择器,可内嵌到程序中,而无需使用系统或三方文件选择器。
-项目地址:https://github.com/iPaulPro/aFileChooser
-
-1. androidpn
-基于 xmpp 协议的消息推送解决方案,包括服务器端和 android 端。
-项目地址:https://github.com/dannytiehui/androidpn
-
-1. Bolts
-Android 的异步编程模式
-项目地址:https://github.com/BoltsFramework/Bolts-Android/
-与 AsyncTask 比较:(1) 使用的是无大小限制的线程池
-(2) 任务可组合可级联,防止了代码耦合
-
-1. CastCompanionLibrary-android
-使 Android 程序中更快的接入 Google Cast
-项目地址:https://github.com/googlecast/CastCompanionLibrary-android
-文档介绍:https://developers.google.com/cast/
-
-1. CastVideos-android
-从 Android 设备分享 Video 通过 Google Cast
-项目地址:https://github.com/googlecast/CastVideos-android
-文档介绍:https://developers.google.com/cast/
-
-1. Uninstall_Statics
-Android 应用自身被卸载监听及打开浏览器等反馈功能实现
-项目地址:https://github.com/sevenler/Uninstall_Statics
-文档介绍:http://www.cnblogs.com/zealotrouge/p/3157126.html http://www.cnblogs.com/zealotrouge/p/3159772.html
-1. Memento
-保证在系统配置改变时,Activity 中的某些数据可以简单安全的保持不变
-项目地址:https://github.com/mttkay/memento
-文档介绍:https://github.com/mttkay/memento#usage
-
-1. FreeFlow
-布局引擎,更简单的创建自定义布局,并且当数据和布局改变时更美观的过渡动画
-项目地址:https://github.com/Comcast/FreeFlow
-Demo 地址:https://github.com/Comcast/FreeFlow/releases
-
-1. Android Gesture Detectors Framework
-Android 手势框架,支持双指旋转、移动、平移、缩放等
-项目地址:https://github.com/Almeros/android-gesture-detectors
-
-1. Mapbox Android SDK
-Android Map 的替代版
-项目地址:https://github.com/mapbox/mapbox-android-sdk
-
-1. Activity animation
-Activity 跳转动画,支持各个方向波浪的效果
-项目地址:https://github.com/flavienlaurent/activityanimation
-在线演示:https://www.youtube.com/watch?v=-E0sc6w_Jck
-
-1. KryoNet
-通过 NIO 提供客户端和服务器端 TCP/UDP 网络传输的 Java 库
-项目地址:https://github.com/EsotericSoftware/kryonet
-
-1. Rebound
-一个模仿弹簧反弹的 Java 库,可用于创建动画
-项目地址:https://github.com/facebook/rebound
-
-1. Android Social Networks
-社交网络接入统一管理器,可方便的从 Twitter, LinkedIn, Facebook, Google Plus 登陆、获得个人信息、发送消息、发送专篇、添加或删除好友
-项目地址:https://github.com/antonkrasov/AndroidSocialNetworks
-Demo 地址:https://play.google.com/store/apps/details?id=com.github.androidsocialnetworks.apidemos
-
-1. SmartAppUpdates
-Android App 增量升级,包含前后端方案、Demo、以及 so 库,可用于商店或大体积 App 差分升级
-项目地址:https://github.com/cundong/SmartAppUpdates
-
-1. Magnet
-创建类似 Facebook 聊天桌面悬浮窗的效果
-项目地址:https://github.com/premnirmal/Magnet
-
-1. AcDisplay
-将 Android 的通知都集中到锁屏显示
-项目地址:https://github.com/AChep/AcDisplay
-Demo 地址:https://play.google.com/store/apps/details?id=com.achep.acdisplay
-
-1. QrCodeScan
-ZXing 和 ZBar 结合的二维码扫描项目,提高了扫描效率
-项目地址:https://github.com/SkillCollege/QrCodeScan
-
-1. Android-ScreenShot
-实现 android 系统截屏功能
-项目地址:https://github.com/Android-ScreenShot/AndroidScreenShotService
-文档介绍:http://blog.csdn.net/buptgshengod/article/details/39155979
-
-1. card.io SDK for Android
-信用卡扫描 Android SDK
-项目地址:https://github.com/card-io/card.io-Android-SDK
-Demo 项目:https://github.com/card-io/card.io-Android-SDK/tree/master/SampleApp
-
-1. ASNE
-Twitter, Facebook, Google Plus, LinkedIn, Instagram, Vkontakte, Odnoklassniki 的集成库,包括他们的大多数功能
-项目地址:https://github.com/gorbin/ASNE
-Demo 地址:https://play.google.com/store/apps/details?id=com.gorbin.androidsocialnetworksextended.asne
-
-1. Android Signature Pad
-Android 自定义的签名 View,可自定义笔颜色和大小
-项目地址:https://github.com/gcacace/android-signaturepad
-Demo 项目:https://github.com/gcacace/android-signaturepad/tree/master/SignaturePad-Example
-
-1. Teleport
-Android Wear 数据同步和消息传送库
-项目地址:https://github.com/Mariuxtheone/Teleport
-
-1. DebugLog
-可以帮你创建更简单和更容易理解的调试日志,能够友好的显示调试信息所在类和函数。
-项目地址:https://github.com/MustafaFerhan/DebugLog
-
-1. Logger
-简单、美观而且十分强大的 Android 日志工具
-项目地址:https://github.com/orhanobut/logger
-Demo 地址:https://github.com/orhanobut/logger/tree/master/app
-
-1. jlog
-可以灵活配置的日志工具,支持JSON打印,日志保存到指定文件,日志显示调用位置(混淆模式下依然有效)
-项目地址:https://github.com/JiongBull/jlog
-Demo 地址:[apk地址](https://raw.githubusercontent.com/JiongBull/jlog/master/apk/JLog_sample.apk)
-
-1. Phrase
-square 组开源大牛写的字符串替换类
-项目地址:https://github.com/square/phrase
-
-1. ColorPhrase
-模仿 phrase 写的对字符串个别字符颜色改变的类,高效强大!
-项目地址:https://github.com/THEONE10211024/ColorPhrase
-
-1. PinyinSearch
-一个为 T9 搜索和 Qwerty 搜索,提供数据解析与匹配等方法的拼音搜索 java 库。
-项目地址: https://github.com/handsomezhou/PinyinSearchLibrary
-Demo 地址: [ContactsSearch](https://github.com/handsomezhou/PinyinSearchLibrary/blob/master/external_res/bin/ContactsSearch.apk?raw=true) [AppSearch](https://github.com/handsomezhou/PinyinSearchLibrary/blob/master/external_res/bin/AppSearch.apk?raw=true)
-APP 示例: 通讯录应用,微信电话本
-
-1. PrainView
-PaintView 是一个画图工具:
-可直接使用设定按钮来实现已拥有的方法,且拓展性强
-基础功能:更换颜色、更换橡皮、以及更换橡皮和笔的粗细、清屏、倒入图片
-特殊功能:保存画笔轨迹帧动画、帧动画导入导出、ReDo和UnDo
-项目地址:[Paintview](https://github.com/lfkdsk/JustWeTools)
-demo地址:[apk地址](https://github.com/lfkdsk/JustWeTools/blob/master/demo/DrawAPicture.apk)
-原理解析:[PaintView 绘图控件解析](http://www.cnblogs.com/lfk-dsk/p/4768850.html)
-
-1. CodeView
-CodeView代码查看/修改工具: 基于WebView制作的代码编辑器,实现代码高亮,暗色主题
-项目地址:[CodeView](https://github.com/lfkdsk/JustWeTools)
-demo地址:[apk地址](https://github.com/lfkdsk/JustWeTools/blob/master/demo/JustWeTools.apk)
-
-1. ExplorerView
-文件浏览器:继承自ListView,可拓展性强,可进行文件类型分析
-项目地址:[CodeView](https://github.com/lfkdsk/JustWeTools)
-demo地址:[apk地址](https://github.com/lfkdsk/JustWeTools/blob/master/demo/JustWeTools.apk)
-
-1. ReadView
-小说阅读器,支持文字字体、颜色、背景、进度等多种调整,支持一键设置。
-项目地址:[ReadView](https://github.com/lfkdsk/JustWeTools)
-demo地址:[apk地址](https://github.com/lfkdsk/JustWeTools/blob/master/demo/JustWeTools.apk)
-
-1. MarkDownView
-支持MarkDown语法的渲染器,基于WebView的MarkDown渲染器,支持标准化的MarkDown语法,
-调用接口和CodeView保持一致使用简便
-项目地址:[ReadView](https://github.com/lfkdsk/JustWeTools)
-demo地址:[apk地址](https://github.com/lfkdsk/JustWeTools/blob/master/demo/JustWeTools.apk)
-
-1. VerTextView竖行排版的TextView:
-支持竖行排版,添加了下划线功能,开启简便,下划线粗细、颜色、间距均可自定义,
-接口调用方式与TextView相似,使用简便。
-项目地址:[ReadView](https://github.com/lfkdsk/JustWeTools)
-demo地址:[apk地址](https://github.com/lfkdsk/JustWeTools/blob/master/demo/JustWeTools.apk)
-
-1. GHDownload下载框架
-支持单线程和多线程断点下载,简单易用。
-项目地址:[GHDownload](https://github.com/guanchao/GHDownload)
-
-
-## 第三部分 优秀项目
-主要介绍那些 Android 还不错的完整项目,目前包含的项目主要依据是项目有意思或项目分层规范比较好。
-
-### 一、系统及平台
-1. Linux
-项目地址:https://github.com/torvalds/linux
-1. Android
-项目地址:https://android.googlesource.com/
-
-### 二、项目
-1. ZXing
-二维码扫描工具
-项目地址:https://github.com/zxing/zxing 或 https://code.google.com/p/zxing/
-APK 地址:https://play.google.com/store/apps/details?id=com.google.zxing.client.android
-其他:现在市面上很多应用的二维码扫描功能都是从这个修改而来
-
-1. photup
-编辑机批量上传照片到 facebook 上
-项目地址:https://github.com/chrisbanes/photup
-APK 地址:https://play.google.com/store/apps/details?id=uk.co.senab.photup
-其他:代码分包合理,很棒。不过这个项目依赖的开源项目比较多,比较难编译
-
-1. github-android
-Github 的 Android 客户端项目
-项目地址:https://github.com/github/android
-APK 地址:https://play.google.com/store/apps/details?id=com.github.mobile
-
-1. Notes
-MIUI 便签
-项目地址:https://github.com/MiCode/Notes
-APK 地址:https://github.com/Trinea/TrineaDownload/blob/master/miui-note-demo.apk?raw=true
-其他:项目分包比较合理,相比较 miui 的文件管理器 https://github.com/MiCode/FileExplorer 代码规范较好得多
-
-1. weicuiyuan
-四次元-新浪微博客户端
-项目地址:https://github.com/qii/weiciyuan
-APK 地址:https://play.google.com/store/apps/details?id=org.qii.weiciyuan
-
-1. Douya
-豆芽——Material Design 的豆瓣客户端
-项目地址:https://github.com/DreaminginCodeZH/Douya
-
-1. gnucash-android
-一个记账理财软件
-项目地址:https://github.com/codinguser/gnucash-android
-APK 地址:http://play.google.com/store/apps/details?id=org.gnucash.android
-
-1. AntennaPod
-支持 rss 订阅、音乐订阅
-项目地址:https://github.com/danieloeh/AntennaPod
-APK 地址:https://play.google.com/store/apps/details?id=de.danoeh.antennapod
-
-1. ChaseWhisplyProject
-打鬼游戏
-项目地址:https://github.com/tvbarthel/ChaseWhisplyProject
-APK 地址:https://play.google.com/store/apps/details?id=fr.tvbarthel.games.chasewhisply
-
-1. Tweet Lanes
-功能完整的 Twitter 客户端
-项目地址:https://github.com/chrislacy/TweetLanes
-APK 地址:https://play.google.com/store/apps/details?id=com.tweetlanes.android
-
-1. Financius
-简单易用的记账程序
-项目地址:https://github.com/mvarnagiris/Financius
-APK 地址:https://play.google.com/store/apps/details?id=com.code44.finance
-
-1. todo.txt-android
-todo.txt 的官方 Android 应用
-项目地址:https://github.com/ginatrapani/todo.txt-android
-APK 地址:https://play.google.com/store/apps/details?id=com.todotxt.todotxttouch
-
-1. simpletask
-基于 todo.txt 官方应用的另一个客户端
-项目地址:https://github.com/mpcjanssen/simpletask-android
-APK 地址:https://play.google.com/store/apps/details?id=nl.mpcjanssen.todotxtholo
-
-1. Muzei Live Wallpaper
-定时更换桌面精美壁纸
-项目地址:https://github.com/romannurik/muzei
-APK 地址:https://play.google.com/store/apps/details?id=net.nurik.roman.muzei
-
-1. Scanbook
-扫描搜索查询图书信息
-项目地址:https://github.com/JayFang1993/ScanBook
-APK 地址:http://www.wandoujia.com/apps/com.scanbook
-
-1. iosched
-The Google I/O 2014 Android App
-项目地址:https://github.com/google/iosched
-APK 地址:https://play.google.com/store/apps/details?id=com.google.samples.apps.iosched
-
-## 第四部分 开发工具及测试工具
-主要介绍和 Android 开发工具和测试工具相关的开源项目。
-
-### 一、开发效率工具
-1. Parceler
-通过注解及工具类自动完成实体类 Parcelable 及值传递
-项目地址:https://github.com/johncarl81/parceler
-
-1. Json2Java
-根据 JSon 数据自动生成对应的 Java 实体类,还支持 Parcel、Gson Annotations 对应代码自动生成。期待后续的提取父类以及多 url 构建整个工程的功能
-项目地址:https://github.com/jonfhancock/JsonToJava
-在线演示:http://jsontojava.appspot.com/
-
-1. IntelliJ Plugin for Android Parcelable boilerplate code generation
-Android studio 插件,生成 Parcelable 代码
-项目地址:https://github.com/mcharmas/android-parcelable-intellij-plugin
-
-1. Android Holo Colors IntelliJ Plugin
-Android studio 插件,生成 holo 样式 9 patch 图片
-项目地址:https://github.com/jeromevdl/android-holo-colors-idea-plugin
-
-1. Android Drawable Factory
-用于生成各个分辨率的图片
-项目地址:https://github.com/tizionario/AndroidDrawableFactory
-
-1. SelectorChapek for Android
-Android Studio 插件,可根据固定文件名格式资源自动生成 drawable selectors xml 文件。
-项目地址:https://github.com/inmite/android-selector-chapek
-
-1. Android Action Bar Style Generator
-Android ActionBar 样式生成器,可在线选择 ActionBar 样式自动生成所需要的图片资源及 xml 文件
-项目地址:https://github.com/jgilfelt/android-actionbarstylegenerator
-在线演示:http://jgilfelt.github.io/android-actionbarstylegenerator/
-
-1. ButterKnifeZelezny
-用于快速生成[ButterKnife](https://github.com/JakeWharton/butterknife)View 注入代码的 Android Studio/IDEA 插件
-项目地址:https://github.com/inmite/android-butterknife-zelezny
-
-1. RoboCoP
-利用 Gradle task 根据固定格式的 json 文件生成 ContentProvider
-项目地址:https://github.com/mediarain/RoboCoP
-
-1. appiconsizes
-用于生成各个分辨率的图片
-项目地址:http://www.appiconsizes.com/
-
-1. AndroidUtiles
-集合了所有常用的安卓工具类
-项目地址:https://github.com/l123456789jy/Lazy
-
-1. Gradle Retrolambda Plugin
-[Retrolambda](https://github.com/orfjackal/retrolambda)是将 Java8 的 Lambdas 应用于 Java7 的工具,本项目是 Gradle 插件,通过 Retrolambda 从而使 Java 或 Android 项目用 Java8 的 Lambdas 编写,将编译后的字节码转换为 Java6 和 7 的字节码从而正常运行
-项目地址:https://github.com/evant/gradle-retrolambda
-
-1. Dagger IntelliJ Plugin
-dagger 的 intellij 插件
-项目地址:https://github.com/square/dagger-intellij-plugin
-
-1. Android Gen Drawable Maven plugin
-在编译时根据 SVG 描述文件生成不同分辨率的 jpg、png 或点 9 图片
-项目地址:https://github.com/avianey/androidgendrawable-maven-plugin
-
-1. Android Asset Studio
-各种 Android 资源自动生成器,包括启动图标、ActionBar 图标、通知栏图标、点 9 等
-项目地址:https://github.com/romannurik/AndroidAssetStudio
-在线演示:http://romannurik.github.io/AndroidAssetStudio/
-
-1. jsonschema2pojo
-根据 Json 内容生成 java 对象,支持 jackjson 和 gson
-项目地址:https://github.com/joelittlejohn/jsonschema2pojo
-在线演示:http://www.jsonschema2pojo.org/
-
-1. GsonFormat
-根据 JSONObject 格式的字符串,自动生成实体类参数的 Android Studio/IntelliJ IDEA 插件
-项目地址:https://github.com/zzz40500/GsonFormat
-
-1. 9-Patch-Resizer
-自动生成 png 及点 9 图片的不同分辨率版本
-项目地址:https://github.com/redwarp/9-Patch-Resizer
-
-1. AndroidLocalizationer
-可用于将项目中的 string 资源自动翻译为其他语言的 Android Studio/IntelliJ IDEA 插件
-项目地址:https://github.com/westlinkin/AndroidLocalizationer
-
-1. Material Palette
-一个将现有的色彩方案组合成材料设计的调色板方案,提供下载。选取两个主要色彩后便可以提供下载,提供下载的格式可以是 android 的资源 xml 文件,以色彩的方式提供,也可以是 CSS、SVG、PNG 等常见格式文件。
-项目地址:http://www.materialpalette.com/
-
-### 二、开发自测相关
-1. Quality Tools for Android
-Android 测试及自测工具集合和示例
-项目地址:https://github.com/stephanenicolas/Quality-Tools-for-Android
-
-1. android-test-kit
-Google 的 Android 测试工具
-包括 GoogleInstrumentationTestRunner(增强版的 InstrumentationTestRunner)和 Espresso(用于快速写出可靠测试用例的 API)
-项目地址:https://code.google.com/p/android-test-kit/
-文档介绍:https://code.google.com/p/android-test-kit/w/list
-
-1. robolectric
-测试用例编写框架
-项目地址:https://github.com/robolectric/robolectric
-Demo 地址:https://github.com/robolectric/robolectricsample
-文档介绍:http://robolectric.org/
-特点:(1). 不需要模拟器在一般 JVM 就可以运行测试用例
-(2). 能完成在真机上的大部分测试包括感应器
-其他的测试用例及相关模块 Mock 可见:[android-mock](https://code.google.com/p/android-mock/), [mockito](https://code.google.com/p/mockito/), [easy-mock](https://github.com/easymock/easymock)
-
-1. Android FEST
-提供一些列方便的断言,可用于提高编写 Android 自测代码效率
-项目地址:https://github.com/square/fest-android
-
-1. BoundBox
-可用于测试类各种访问权限的属性、方法。实际是通过 BoundBox 这个 annotation 生成一个属性和方法都是 public 权限的中间类并对此类进行测试完成的
-项目地址:https://github.com/stephanenicolas/boundbox
-
-1. Hugo
-用于打印函数信息及执行时间的工具,仅在 debug 模式生效
-项目地址:https://github.com/JakeWharton/hugo
-
-1. scalpel
-在应用下面添加一层用于界面调试
-项目地址:https://github.com/JakeWharton/scalpel
-
-1. Android Screenshot library
-Android 截图工具类,用于在持续集成时截图
-项目地址:https://github.com/rtyley/android-screenshot-lib
-
-1. sonar-android-lint-plugin
-将 android lint 的错误在 sonar 中展现
-项目地址:https://github.com/SonarCommunity/sonar-android
-
-### 三、测试工具
-1. Spoon
-可用于 android 不同机型设备自动化测试,能将应用 apk 和测试 apk 运行在不同机器上并生成相应测试报告。
-项目地址:https://github.com/square/spoon
-
-1. Tencent APT
-APT 是腾讯开源的一个 Android 平台高效性能测试组件,提供丰富实用的功能,适用于开发自测、定位性能瓶颈;测试人员完成性能基准测试、竞品对比测试
-项目地址:https://github.com/stormzhang/APT
-
-1. Emmagee
-网易开源的性能测试工具,包括 CPU、内存、网络流量、启动时间、电池状态等
-项目地址:https://github.com/NetEase/Emmagee
-
-1. Android py-uiautomator
-py-uiautomator 是一个对 Android uiautomator 用 python 进行封装的测试框架.
-项目地址:https://github.com/xiaocong/uiautomator
-
-1. Augmented Traffic Control
-模拟网络状况,包括带宽、时延抖动、丢包率、错包率、包重排率
-项目地址:https://github.com/facebook/augmented-traffic-control
-
-1. stetho
-强大的 Android Debug 工具。支持网络请求监控以及数据库查看,可以和 Chrome DevTools 结合或者命令行模式。
-项目地址:https://github.com/facebook/stetho
-
-### 四、开发及编译环境
-1. Buck
-facebook 开源的 Android 编译工具,效率是 ant 的两倍。主要优点在于:
-(1) 加快编译速度,通过并行利用多核 cpu 和跟踪不变资源减少增量编译时间实现
-(2) 可以在编译系统中生成编译规则而无须另外的系统生成编译规则文件
-(3) 编译同时可生成单元测试结果
-(4) 既可用于 IDE 编译也可用于持续集成编译
-(5) facebook 持续优化中
-项目地址:https://github.com/facebook/buck
-
-1. Android Maven Plugin
-Android Maven 插件,可用于对 android 三方依赖进行管理。在 J2EE 开发中,maven 是非常成熟的依赖库管理工具,可统一管理依赖库。
-项目地址:https://github.com/jayway/maven-android-plugin
-
-1. umeng-muti-channel-build-tool
-渠道打包工具
-项目地址:https://github.com/umeng/umeng-muti-channel-build-tool
-另可参见 Google 的构建系统 Gradle:http://tools.android.com/tech-docs/new-build-system/user-guide
-
-1. Genymotion
-目前最好用最快的 android 模拟器
-项目地址:http://www.genymotion.com/
-Android studio 集成控件: http://plugins.jetbrains.com/plugin/7269?pr=idea
-Cyril Mottier 推荐:http://cyrilmottier.com/2013/06/27/a-productive-android-development-environment/
-
-1. gradle-mvn-push
-方便的将 Gradle 的 Artifacts 上传到 Maven 仓库
-项目地址:https://github.com/chrisbanes/gradle-mvn-push
-文档介绍:https://github.com/chrisbanes/gradle-mvn-push#usage
-
-1. Android Emulator Plugin for Jenkins
-Android 模拟器 jenkins 插件,用于 Jenkins 做持续集成时跑模拟器测试
-项目地址:https://github.com/jenkinsci/android-emulator-plugin
-
-1. Android Maven Plugin
-管理应用所需要的依赖库。包括的构建工具有 Maven、Gradle、ant、sbt
-项目地址:https://github.com/mosabua/maven-android-sdk-deployer
-
-1. SDK Manager Plugin
-下载和管理 Android SDK 的 Gradle 插件
-项目地址:https://github.com/JakeWharton/sdk-manager-plugin
-
-1. Gradle Protobuf Plugin
-将.proto 文件转换成 Java 文件的 gradle 插件
-项目地址:https://github.com/andrewkroh/gradle-protobuf-plugin
-
-1. ChromeADB
-Chrome 的 Adb 插件,当登录后,能看到所有连接的设备并操作,可以看应用、进程、内存及磁盘使用情况等
-项目地址:https://github.com/importre/chromeadb
-
-### 五、其他
-1. GT
-APP的随身调试平台,它是直接运行在手机上的“集成调试环境”(IDTE, Integrated Debug Environment)。
-项目地址:https://github.com/TencentOpen/GT
-文档介绍:http://gt.qq.com/docs.html
-
-1. ViewServer
-允许 app 运行在任何手机上都可以用 HierarchyViewer 查看
-项目地址:https://github.com/romainguy/ViewServer
-
-1. GridWichterle for Android
-在整个系统上显示一个 grid,用来帮助查看应用布局及使得布局更美观,可设置 grid 网格大小和颜色,android 推荐 48dp 和 8dp,可见 Android Design Guidelines – Metrics and Grids,比起 hierarchyviewer 相差甚远,不过偶尔可用来作为布局查看工具。
-项目地址:https://github.com/inmite/android-grid-wichterle
-Demo 地址:https://play.google.com/store/apps/details?id=eu.inmite.android.gridwichterle
-
-1. Catlog
-手机端 log 查看工具,支持不同颜色显示、关键字过滤、级别过滤、进程 id 过滤、录制功能等
-项目地址:https://github.com/nolanlawson/Catlog
-在线演示:https://play.google.com/store/apps/details?id=com.nolanlawson.logcat
-
-1. PID Cat
-根据 package 查看 logcat 日志
-项目地址:https://github.com/JakeWharton/pidcat
-
-1. ACRA
-应用崩溃信息日志上报到 GoogleDoc 工具,网页版展现结果三方开源地址 https://github.com/BenoitDuffez/crashreportsviewer
-项目地址:https://github.com/ACRA/acra
-文档介绍:https://github.com/ACRA/acra/wiki/BasicSetup
-
-1. Crashlytics
-提供丰富的应用崩溃信息日志收集
-轻量级,丰富,可自定义应用崩溃信息收集器,附有邮件通知
-项目地址:http://www.crashlytics.com/
-集成插件:[Android Studio, Eclipse and IntelliJ](http://try.crashlytics.com/sdk-android/?utm_source=blog&utm_medium=blog&utm_campaign=Announcing_android_studio_wp&utm_content=CTA_button)
-
-1. Android Resource Navigator
-chrome 插件,可以方便的查看 github 上 android 源码工程的 styles.xml 和 themes.xml。主要功能:
-(1) 快速打开 android styles.xml themes.xml
-(2) 方便在资源间跳转。styles.xml themes.xml 文件中资源链接跳转,可以方便跳转到某个资源
-(3) 方便查找某个 style 和 theme。chrome 地址栏输入 arn+tab+搜索内容回车即可
-(4) 自动下载不同分辨率下的 drawable
-(5) 通过映射查找那些不是按照固定命名规则命名的 style 和 theme
-项目地址:https://github.com/jgilfelt/android-resource-navigator
-在线演示:https://chrome.google.com/webstore/detail/android-resource-navigato/agoomkionjjbejegcejiefodgbckeebo?hl=en&gl=GB
-
-1. android-resource-remover
-根据 lint 的提示删除项目中无用的资源,减少包的大小
-项目地址:https://github.com/KeepSafe/android-resource-remover
-
-1. Telescope
-通过手势截图以特定主题发送到特定邮箱地址报告 Bug
-项目地址:https://github.com/mattprecious/telescope
-
-1. Complete Android Fragment & Activity Lifecycle
-完整的 Android Fragment/Activity 生命周期图
-项目地址:https://github.com/xxv/android-lifecycle
-
-1. Bugsnag Notifier for Android
-通过`Thread.UncaughtExceptionHandler`捕获应用未处理的异常崩溃 Bug 并用 Notification 展示同时上传到后台服务器
-项目地址:https://github.com/bugsnag/bugsnag-android
-文档介绍:https://github.com/bugsnag/bugsnag-android#installation--setup
-
-1. Material Design Icons
-Google Material Design 规范中的 Icon
-项目地址:https://github.com/google/material-design-icons
-
-1. scrollscreenshot
-Android 滚动屏幕自动截图 jar 包,支持纵向、横向滚动截屏拼接
-项目地址:https://github.com/PGSSoft/scrollscreenshot
-
-1. droidicon
-1600+的海量 Icon,包括 750+的 Material Design icons
-项目地址:https://github.com/theDazzler/droidicon
-
-1. leakcanary
-Android 内存泄露检测工具,集成方便,出现泄露后报告直观
-项目地址:https://github.com/square/leakcanary
-
-1. CacheUtilsLibrary
-将任何Java Object类型的数据序列化后写入缓存文件,将来使用时读取缓存文件并反序列化成对应Java Object的库
-项目地址:https://github.com/westlinkin/CacheUtilsLibrary
-
-1. BlockCanary
-BlockCanary是一个Android平台的一个非侵入式的性能监控组件,应用只需要实现一个抽象类,提供一些该组件需要的上下文环境,就可以在平时使用应用的时候检测主线程上的各种卡慢问题,并通过组件提供的各种信息分析出原因并进行修复。
-项目地址: https://github.com/moduth/blockcanary
-
-## 第五部分
-主要介绍那些乐于分享并且有一些很不错的开源项目的个人和组织。Follow 大神,深挖大神的项目和 following,你会发现很多。
-
-### 一、个人
-1. JakeWharton
-就职于 Square,绝对牛逼的大神,项目主要集中在 Android 版本兼容,ViewPager 及开发工具上
-Github 地址:https://github.com/JakeWharton
-代表作:ActionBarSherlock,Android-ViewPagerIndicator,Nine Old Androids,SwipeToDismissNOA,hugo,butterknife,Android-DirectionalViewPager, scalpel
-pidcat 另外对 square 及其他开源项目有很多贡献
-主页:http://jakewharton.com/
-
-1. Chris Banes
-Github 地址:https://github.com/chrisbanes
-代表作:ActionBar-PullToRefresh,PhotoView,Android-BitmapCache,Android-PullToRefresh
-主页:http://chris.banes.me/
-
-1. Koushik Dutta
-就职于 ClockworkMod
-Github 地址:https://github.com/koush
-代表作:Superuser,AndroidAsync,UrlImageViewHelper,ion, 另外对 https://github.com/CyanogenMod 的开源项目有很多贡献
-主页:http://koush.com/
-
-1. Simon Vig
-Github 地址:https://github.com/SimonVT
-代表作:android-menudrawer,MessageBar
-主页:http://simonvt.net/
-
-1. Manuel Peinado
-Github 地址:https://github.com/ManuelPeinado
-代表作:FadingActionBar,GlassActionBar,RefreshActionItem,QuickReturnHeader
-
-1. Emil Sj?lander
-Github 地址:https://github.com/emilsjolander
-代表作:StickyListHeaders,sprinkles,android-FlipView
-主页:http://emilsjolander.se/
-
-1. greenrobot
-Github 地址:https://github.com/greenrobot
-代表作:greenDAO,EventBus
-主页:http://greenrobot.de/
-
-1. Jeff Gilfelt
-Github 地址:https://github.com/jgilfelt
-代表作:android-mapviewballoons,android-viewbadger,android-actionbarstylegenerator,android-sqlite-asset-helper
-主页:http://jeffgilfelt.com
-
-1. Romain Guy
-Android team 成员(2013.10 已离开 Android team,仍在 Google)
-Github 地址:https://github.com/romainguy
-代表作:ViewServer
-主页:http://www.curious-creature.org/category/android/
-個人攝影作品:http://www.flickr.com/photos/romainguy
-
-1. sephiroth74
-就职于 Aviary.com
-Github 地址:https://github.com/sephiroth74
-代表作:ImageViewZoom,HorizontalVariableListView,AndroidWheel,purePDF
-主页:http://www.sephiroth.it/
-
-1. Cyril Mottier
-Google 开发者专家认证,发布一些 Android 技巧及文章
-Github 地址:https://github.com/cyrilmottier
-代表作:GreenDroid,Polaris
-主页:http://cyrilmottier.com/
-
-### 二、组织
-1. Square
-有态度有良心的企业,很多不错的分享
-Github 地址:https://github.com/square
-代表作:okhttp、fest-android,android-times-square、picasso、dagger、spoon 等等
-主页:http://square.github.io/
-
-1. Inmite s.r.o.
-Github 地址:https://github.com/inmite
-代表作:android-styled-dialogs,android-grid-wichterle,android-selector-chapek
-主页:http://www.inmite.eu/
-
-### 三、博客
-[部分国外著名 Android 开发者信息](https://github.com/android-cn/android-dev-com)
-
-## 注意
-> 本文转自——**[Trinea 汇总的开源项目](https://github.com/Trinea/android-open-project)**
-> 欢迎大家关注他的微信公众号:codekk。二维码如下:
-> 专注于 Android 开源分享、源码解析、框架设计、Android 内推。
-> 
-> 他的网站:[www.codekk.com](http://a.codekk.com)
-> **关于trinea,欢迎关注**
-> 微博:Trinea 主页:trinea.cn
-
-## 推荐
-> [Android 开源项目源码解析(Volley、UIL、Dagger、EventBus、插件化库等分析)](http://a.codekk.com)
->
-> [Android 职位内部推荐(阿里、腾讯、百度、京东、滴滴、美团、58、华为、网易、魅族等)](http://j.codekk.com)
\ No newline at end of file
diff --git a/source/_posts/2016-04-05.coding-standards.md b/source/_posts/2016-04-05.coding-standards.md
deleted file mode 100644
index d922036f..00000000
--- a/source/_posts/2016-04-05.coding-standards.md
+++ /dev/null
@@ -1,129 +0,0 @@
----
-title: 实际项目中的代码规范
-comments: true
-fancybox: true
-toc: false
-date: 2016-04-05 21:35:53
-tags:
-- eclipse
-- Android
-categories:
-- Android
-description:
-permalink: coding-standards
-top:
----
-前言 最近在准备软件杯的比赛项目,因为是担任的是项目组长,所以要求组员之间的项目代码必须要有规范,方便组员之间的团队合作,以及后期代码的维护。而且,我相信,如果我们代码规范能够做好的话,对于我们的开发水平也会提高多少。
-
-## 1. 代码的规范化好处多多
-
-> 1. 规范的代码可以促进团队合作
-> 1. 规范的代码可以减少bug处理
-> 1. 规范的代码可以降低维护成本
-> 1. 规范的代码有助于代码审查
-> 1. 养成代码规范的习惯,有助于程序员自身的成长
-
-## 2. 开发前的准备
-
-Eclipse中设置代码样式在 `Window -->Preferences` 中设置
-### 2.1. 设置工作空间编码为UTF-8
-
-
-### 2.2. 设置文件编码为UTF-8
-
-
-
-
-### 2.3. 方法变量名转化成成员变量名称时,变量名称前加m以示区分
-
-
-### 2.4. 配置文档注释
-
- `Window -->Preferences -->Java -->Code Style -->Code Templates`
-#### 2.4.1. Comments
-
-**Files修改成如下内容**
-
- /**
- * @Title: ${file_name}
- * @Package ${package_name}
- * @Description: ${todo}(用一句话描述该文件做什么)
- * @author Yangkai.Shen(此处换成你自己的名字,格式参考我的)
- * @date ${date} ${time}
- * @version V1.0
- */
-
-**Types修改成如下内容**
-
- /**
- * @ClassName: ${type_name}
- * @Description: ${todo}(这里用一句话描述这个类的作用)
- * @author Yangkai.Shen(此处换成你自己的名字,格式参考我的)
- * @date ${date} ${time}
- * ${tags}
- */
-
-**Fields修改成如下内容**
-
- /**
- * @Fields ${field} : ${todo}(用一句话描述这个变量表示什么)
- */
-**Constructors修改成如下内容**
-
- /**
- * Title:
- * Description:
- * ${tags}
- */
-**Methods修改成如下内容**
-
- /**
- * @Title: ${enclosing_method}
- * @Description: ${todo}(这里用一句话描述这个方法的作用)
- * ${tags} 参数说明
- * ${return_type} 返回类型
- * @throws
- */
-**Overriding methods修改成如下内容**
-
- /*
- * Title: ${enclosing_method}
- * Description:
- * ${tags}
- * ${see_to_overridden}
- */
-**Delegate methods修改成如下内容**
-
- /**
- * ${tags}
- * ${see_to_target}
- */
-**Getters修改成如下内容**
-
- /**
- * @return the ${bare_field_name}
- */
-**Setters修改成如下内容**
-
- /**
- * @param ${param} the ${bare_field_name} to set
- */
-
-#### 2.4.2. Codes中只修改New Java files
-
-**New Java files修改成如下内容**
-
- /**
- * @Title: ${file_name}
- * @Package ${package_name}
- * @Description: ${todo}(用一句话描述该文件做什么)
- * @author Yangkai.Shen(此处换成你自己的名字,格式参考我的)
- * @date ${date} ${time}
- * @version V1.0
- */
-## 3. 开发时的规范
-
-1. 每个方法前添加文档注释(快捷方式:选中需要添加的方法名,再按alt+shift+j)
-2. SVN提交代码时,必须注明提交代码时所作的修改
-3. 尽可能少的使用全局的静态变量,以免常驻内存
-4. 方法变量名转化成成员变量名称时,变量名称前加m以示区分(可以在eclipse中已经设置,自动化解决)
diff --git a/source/_posts/2016-12-13.elasticsearch-get-start.md b/source/_posts/2016-12-13.elasticsearch-get-start.md
deleted file mode 100644
index 62c026f4..00000000
--- a/source/_posts/2016-12-13.elasticsearch-get-start.md
+++ /dev/null
@@ -1,109 +0,0 @@
----
-title: elasticsearch环境搭建及起步
-date: 2016-12-13 17:12:39
-tags:
-- java
-- elasticsearch
-categories:
-- 技术相关
-- elasticsearch
-comments: true
-fancybox: false
-description:
-toc: true
-permalink: elasticsearch-get-start
-top:
----
-## ElasticSearch 概述
-> ElasticSearch是一个高可扩展的开源的全文搜索分析引擎。
-> - 它允许你快速的存储、搜索和分析大量数据。ElasticSearch通常作为后端程序,为需要复杂查询的应用提供服务。
-> - Elasticsearch是一个基于Lucene的开源分布式搜索引擎,具有分布式多用户能力。Elasticsearch是用java开发,提供Restful接口,能够达到实时搜索、高性能计算;同时Elasticsearch的横向扩展能力非常强,不需要重启服务,基本上达到了零配置。
-
-
-## ElasticSearch 安装
-1. 在VMware上安装centos7
-2. 安装vim,输入`yum install -y vim`
-3. 安装git,输入:`yum install -y git`
-4. 安装jdk1.8+
- - 查看CentOS自带JDK是否已安装,输入:`yum list installed |grep java`。
- - 若有自带安装的JDK,卸载CentOS系统自带Java环境。
- - 卸载JDK相关文件,输入:`yum -y remove java-1.8.0-openjdk*`。
- - 卸载tzdata-java,输入:`yum -y remove tzdata-java.noarch`。当结果显示为Complete!即卸载完毕。
-
- > 注:“*”表示卸载掉java1.8.0的所有openjdk相关文件。
-
- - 新建目录`mkdir /usr/java/`
- - 将下载的jdk的rpm包上传至`/usr/java`文件夹中,修改权限,输入`chmod +x jdk-8u111-linux-x64.rpm`
- - 输入`rpm -ivh jdk-8u101-linux-x64.rpm`安装
- - 输入`PATH="${PATH}:/usr/java/jdk1.8.0_111"`,修改环境变量
- - 查看刚安装的Java版本信息,输入:`java -version` 可查看Java版本。
-5. 打开xshell通过xftp将elasticsearch-2.1.1.tar.gz传到虚拟机`/opt`中,cd到opt目录,输入`tar zxvf elasticsearch-2.1.1.tar.gz`,解压至当前目录。
- > 注:不可以使用root用户启动elasticsearch,这是出于系统安全考虑设置的条件。由于ElasticSearch可以接收用户输入的脚本并且执行,为了系统安全考虑,建议创建一个单独的用户用来运行xkcoding
- >
- > 如果是用root账号启动,会报以下错误
- >
- ```java
- Exception in thread "main" java.lang.RuntimeException: don't run elasticsearch as root. at org.elasticsearch.bootstrap.Bootstrap.initializeNatives(Bootstrap.java:93) at org.elasticsearch.bootstrap.Bootstrap.setup(Bootstrap.java:144) at org.elasticsearch.bootstrap.Bootstrap.init(Bootstrap.java:285) at org.elasticsearch.bootstrap.Elasticsearch.main(Elasticsearch.java:35) Refer to the log for complete error details.
- ```
-6. 创建xkcoding用户组及xkcoding用户
- ```bash
- groupadd xkcoding
- useradd xkcoding -g xkcoding -p elasticsearch-2.1.1
- ```
-7. 更改elasticsearch-2.1.1文件夹及内部文件的所属用户及组为xkcoding:xkcoding
- ```bash
- cd /opt
- chown -R xkcoding:xkcoding elasticsearch-2.1.1
- ```
-8. 切换到elsearch用户再启动
- ```bash
- su xkcoding
- cd elasticsearch-2.1.1
- ./bin/elasticsearch -d #以守护进程的方式运行
- ```
-9. 打开一个新的终端进行测试
- ```bash
- curl 'http://localhost:9200/?pretty'
- ```
-10. 查看返回数据
- ```json
- {
- "name" : "node-1",
- "cluster_name" : "togeek",
- "version" : {
- "number" : "2.1.1",
- "build_hash" : "40e2c53a6b6c2972b3d13846e450e66f4375bd71",
- "build_timestamp" : "2015-12-15T13:05:55Z",
- "build_snapshot" : false,
- "lucene_version" : "5.3.1"
- },
- "tagline" : "You Know, for Search"
- }
- ```
-11. 需要从windows本机访问centos7虚拟机,因此需要关闭防火墙
- > CentOS 7.0默认使用的是firewall作为防火墙,使用iptables必须重新设置一下
-
- - 直接关闭防火墙
- ```bash
- systemctl stop firewalld.service #停止firewall
- systemctl disable firewalld.service #禁止firewall开机启动
- ```
- - 设置 `iptables service`
- ```bash
- yum -y install iptables-services
- ```
- - 如果要修改防火墙配置,如增加防火墙端口9200
- ```bash
- vi /etc/sysconfig/iptables
- ```
- - 增加规则
- ```bash
- -A INPUT -m state --state NEW -m tcp -p tcp --dport 9200 -j ACCEPT
- ```
- - 保存退出后
- ```bash
- systemctl restart iptables.service #重启防火墙使配置生效
- systemctl enable iptables.service #设置防火墙开机启动
- ```
- - 最后重启系统使设置生效即可。
-12. 在windows的cmd中测试,返回json,测试通过!
\ No newline at end of file
diff --git a/source/_posts/2017-07-12.jdk-map-source-explore.md b/source/_posts/2017-07-12.jdk-map-source-explore.md
deleted file mode 100644
index c5b2129f..00000000
--- a/source/_posts/2017-07-12.jdk-map-source-explore.md
+++ /dev/null
@@ -1,153 +0,0 @@
----
-title: HashMap 的 JDK 源码探索
-comments: true
-fancybox: true
-toc: false
-date: 2017-07-12 11:26:43
-tags:
-- java
-- 源码探索
-categories:
-- 技术相关
-- java
-description:
-permalink: jdk-map-source-explore
-top:
----
-
-> 缘由:今天好友拿着下面的代码,问我为什么Map.Entry 这个接口没有实现 getKey() 和 getValue() 方法,却可以使用,由此,开启了一番查阅 JDK 源码的旅途....
-
-
-
-```java
-Map map = new HashMap();
-
-map.put(1, "张三");
-map.put(2, "李四");
-map.put(3, "王五");
-map.put(4, "赵六");
-map.put(5, "钱七");
-
-Set set = map.entrySet();
-for (Object object : set) {
- Map.Entry entry = (Map.Entry) object;
- System.out.println(entry.getKey() + "-->" + entry.getValue());
-}
-```
-
-1. 首先,我们看 `map` 对象,这个 `map` 对象是 `HashMap` 的一个实例,然后下面的 `Set set = map.entrySet();` 可以知道这其实用的 `HashMap` 实现的 `entrySet()` 方法,然后我们可以查看 `HashMap` 里 `entrySet()` 的源码
-
- 
-
- 从源码可以看出,这里的返回了一个 `EntrySet` 对象,但是需要注意的是这个 `EntrySet` 是 `HashMap` 里的一个内部类,源码如下:
-
- ```java
- final class EntrySet extends AbstractSet> {
- public final int size() {
- return size;
- }
-
- public final void clear() {
- HashMap.this.clear();
- }
-
- public final Iterator> iterator() {
- return new EntryIterator();
- }
-
- public final boolean contains(Object o) {
- if (!(o instanceof Map.Entry))
- return false;
- Map.Entry,?> e = (Map.Entry,?>) o;
- Object key = e.getKey();
- Node candidate = getNode(hash(key), key);
- return candidate != null && candidate.equals(e);
- }
-
- public final boolean remove(Object o) {
- if (o instanceof Map.Entry) {
- Map.Entry,?> e = (Map.Entry,?>) o;
- Object key = e.getKey();
- Object value = e.getValue();
- return removeNode(hash(key), key, value, true, true) != null;
- }
- return false;
- }
-
- public final Spliterator> spliterator() {
- return new EntrySpliterator<>(HashMap.this, 0, -1, 0, 0);
- }
-
- public final void forEach(Consumer super Map.Entry> action) {
- Node[] tab;
- if (action == null)
- throw new NullPointerException();
- if (size > 0 && (tab = table) != null) {
- int mc = modCount;
- for (int i = 0; i < tab.length; ++i) {
- for (Node e = tab[i]; e != null; e = e.next)
- action.accept(e);
- }
- if (modCount != mc)
- throw new ConcurrentModificationException();
- }
- }
- }
- ```
-
- 从这里我们是可以看出,这个 `EntrySet` 其实是封装的一个 `Node` 类的实体。也就是说我们的 `set` 其实就是这个 `Node` 对象。
-
-2. 现在我们来说说这个 `Node` 对象,`Node` 对象也是 `HashMap` 里的一个内部类,源码如下:
-
- ```java
- static class Node implements Map.Entry {
- final int hash;
- final K key;
- V value;
- Node next;
-
- Node(int hash, K key, V value, Node next) {
- this.hash = hash;
- this.key = key;
- this.value = value;
- this.next = next;
- }
-
- public final K getKey() {
- return key;
- }
-
- public final V getValue() {
- return value;
- }
-
- public final String toString() {
- return key + "=" + value;
- }
-
- public final int hashCode() {
- return Objects.hashCode(key) ^ Objects.hashCode(value);
- }
-
- public final V setValue(V newValue) {
- V oldValue = value;
- value = newValue;
- return oldValue;
- }
-
- public final boolean equals(Object o) {
- if (o == this)
- return true;
- if (o instanceof Map.Entry) {
- Map.Entry,?> e = (Map.Entry,?>)o;
- if (Objects.equals(key, e.getKey()) &&
- Objects.equals(value, e.getValue()))
- return true;
- }
- return false;
- }
- }
- ```
- 可以看出来,这个 `Node` 对象是 `Map.Entry` 的实现类,我们可以看到这个 `Node` 对象实现了 `getKey()` 和 `getValue()` 的方法,所以后面调用的 `entry.getKey()` 以及 `entry.getValue()` 方法其实都是调用的 Node 对象里的`getKey()` 和 `getValue()` 方法,这里就是 `Java` 的多态的一种表现。
-
-3. 至此,打完收枪!
\ No newline at end of file
diff --git a/source/_posts/2017-07-18.win10-download.md b/source/_posts/2017-07-18.win10-download.md
deleted file mode 100644
index 086cf06b..00000000
--- a/source/_posts/2017-07-18.win10-download.md
+++ /dev/null
@@ -1,48 +0,0 @@
----
-title: Windows10 系统下载与激活
-comments: true
-fancybox: true
-toc: false
-date: 2017-07-18 09:57:35
-tags:
-- windows
-- 系统
-categories:
-- 系统
-description:
-permalink: win10-download
-top:
----
-
-windows 是目前我们用的最多一个系统,win10 现在也优化的越来越好,今天大姐的电脑有点问题,担心太蠢,不会下载和激活系统,所以整理并记录一下如何下载并激活 win10 系统,以供查阅。
-
-
-1. [微软各类软件下载地址](http://msdn.itellyou.cn/)(国人自己搞得,非微软官方)
-
-2. 选择 win10 更新版本
-
-
-3. 选择 win10 的具体版本
-
-
-4. 复制版本下载地址,使用迅雷下载
-
-我已经把64位多版本的链接单独抠出来了,需要的直接拿去用~
- ```log
- ed2k://|file|cn_windows_10_multiple_editions_version_1703_updated_june_2017_x64_dvd_10724850.iso|5741144064|7717C389428734009E0350A1BB8F2508|/
- ```
-
-5. 附赠一个[激活工具下载地址](http://pan.baidu.com/s/1gfEHU1t) 提取码:`1pdp`
-
- 这个激活工具很强大,可以激活 `windows 系统` 和 `office 软件`,并且提供 `office 2016`和 `office 2013`两个版本的自动安装功能。
-
-6. 激活工具的使用,下载激活工具,解压压缩包,得到一个名为 `KMSTools.exe` 的文件,然后右键,`以管理员身份运行`。(运行之前,注意`关闭所有杀毒软件`!)
-
-
-7. 打开之后,选择第二个 `KMSAuto Lite`
-
-
-8. 弹出另一个界面,点击 `Activate Windows` 选择,然后当下面的蓝框框输出 `ACTIVATION SUCCESSFUL` 的时候,就代表激活成功了。如果卡住了,可以多试几次。
-
-
-9. have fun!
\ No newline at end of file
diff --git a/source/_posts/2017-07-19.spring-boot-mybatis-logback.md b/source/_posts/2017-07-19.spring-boot-mybatis-logback.md
deleted file mode 100644
index 69af1538..00000000
--- a/source/_posts/2017-07-19.spring-boot-mybatis-logback.md
+++ /dev/null
@@ -1,142 +0,0 @@
----
-title: spring-boot整合mybatis的时候使用logback打印日志
-comments: true
-fancybox: true
-toc: false
-date: 2017-07-19 11:44:55
-tags:
-- spring boot
-- mybatis
-categories:
-- 技术相关
-- spring boot
-description:
-permalink: spring-boot-mybatis-logback
-top:
----
-
-> 最近在学习 `spring boot` ,日志用的是 `logback` ,至于为什么没有选常用的 `log4j` ,因为 `logback` 是 `spring boot` 官方推荐的日志系统。但是在整合 `Mybatis` 的时候一直打印不了 `SQL` 语句,查阅多方资料,终于解决了~
->
-
-1. 先展示下 `logback-spring.xml` 的完整内容
-
- ```xml
-
-
-
-
- 【xkcoding】%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] %logger{36} - %msg%n
-
-
-
-
-
-
-
- ${user.dir}/logs/log/online-study.%d.log
-
-
- 【xkcoding】%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] %logger{36} - %msg%n
-
-
-
-
-
- ERROR
- ACCEPT
- DENY
-
-
-
-
- ${user.dir}/logs/error/online-study.%d.error
-
-
- 【xkcoding】%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] %logger{36} - %msg%n
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ```
-
-2. 如果是 Log4J 实现的话可以参考[Mybatis 日志](http://www.mybatis.org/mybatis-3/zh/logging.html)
-
-3. 第一个节点 `consoleLog` 是在控制台显示的 Log 日志,在控制台显示 Log 日志需要在 `appender` 节点里配置 `class="ch.qos.logback.core.ConsoleAppender"` ,格式化输出的配置在 `pattern` 节点中配置,各个参数的介绍如下:
-
- ```
- %-5level: 显示 Log 级别,从左显示5个字符宽度,打印内容: TRACE/DEBUG/INFO/WARN/ERROR
- %d{yyyy-MM-dd HH:mm:ss.SSS}: 显示时间戳,{}中内容代表时间戳格式
- %thread: 显示线程名字
- %logger{36}: 表示 logger 名字最长36个字符,否则按照句点分割
- %msg: 日志消息
- %n: 换行符
- ```
-
-4. 第二个节点 `fileLog` 是将日志保存为文件,需要在 `appender` 节点里配置 ` class="ch.qos.logback.core.rolling.RollingFileAppender"` ,并且指定滚动策略,为每天一个文件,指定文件名为 `%d.log` 。
-
- ```xml
-
-
-
- ${user.dir}/logs/log/online-study.%d.log
-
- ```
-
-5. 第三个节点 `fileErrorLog` 是将错误日志记录在文件中,不保存其他级别的日志,需要配置一个 `filter` 节点。拦截内容如下(`注意单词大小写,千万不能出错!`):
-
- ```xml
-
- ERROR
- ACCEPT
- DENY
-
- ```
-
-6. 在这里再推荐一个 maven 依赖,`lombok`
-
- ```xml
-
- org.projectlombok
- lombok
-
- ```
-
-7. 在 idea 里安装 `lombok` 的相应插件
- 
-
-8. 然后就可以在类名上方添加 `@Slf4j` 注解,这样就可以直接在代码中使用 `log.info("....")` 打印日志了。
-
-9. 再提示下 `lombok` 的其他使用技巧,如果使用了 `lombok` ,只要在实体类的类名上加上 `@Data` 注解,那么我们就不需要写 `getter/ setter/toString` 方法,`lombok` 会自动生成 `getter/setter/toString` 方法,我们直接调用就可以了,如果只需要生成 `getter` 或者 `setter`,就可以直接使用 `@Getter` 或者 `@Setter` 注解。并且 `@Getter` 和 `@Setter` 这两个注解不止可以用在 `Class` 上,还可以用在 `Enum` 上哦。
-
-10. 就介绍到这儿,enjoy it !!!
\ No newline at end of file
diff --git a/source/_posts/2017-11-01.zookeeper-cluster.md b/source/_posts/2017-11-01.zookeeper-cluster.md
deleted file mode 100644
index 94522ddd..00000000
--- a/source/_posts/2017-11-01.zookeeper-cluster.md
+++ /dev/null
@@ -1,248 +0,0 @@
----
-title: dubbo前置知识:zookeeper集群搭建
-comments: true
-fancybox: true
-toc: false
-date: 2017-11-01 13:55:10
-tags:
-- zookeeper
-- dubbo
-- linux
-categories:
-- 技术相关
-- java
-description:
-permalink: zookeeper-cluster
-top:
----
-
-拖了一天一天又一天,终于在时隔3月之后,又迎来了一篇新博客。真的是懒癌晚期了。o(╯□╰)o
-
-> `dubbo` 框架需要将服务统一注册到一个注册中心,才能被远程调用。这里注册中心可以使用 `zookeeper` 或者 `redis`。
-
-这里我们以 `zookeeper` 为例。`zookeeper` 可以单机版也就是 standalone模式运行,也可以使用集群方式运行。为了实现高可用,本例采用集群方式部署,需要保证至少半数以上的机器存活,所以至少为3台机器。话不多说,让我们开始 `zookeeper` 集群的搭建吧:
-
-
-
-1. 官网下载 `zookeeper-3.4.10.tar.gz` ,将软件包分别放在三台 Linux 虚拟机的 `/software` 目录下。
-2. 进入 `software` 目录下,解压软件包。
-
- ```Shell
- [root@localhost ~]# cd /software
- [root@localhost software]# tar -zxvf zookeeper-3.4.10.tar.gz
- [root@localhost software]# ll
- total 34228
- drwxr-xr-x. 10 1001 1001 4096 Mar 23 2017 zookeeper-3.4.10
- -rw-r--r--. 1 root root 35042811 Nov 1 14:58 zookeeper-3.4.10.tar.gz
- ```
-
-3. 解压后的文件夹名字包含了版本号,我们将其删除。
-
- ```Shell
- [root@localhost software]# mv zookeeper-3.4.10 zookeeper
- [root@localhost software]# ll
- total 34228
- drwxr-xr-x. 10 1001 1001 4096 Mar 23 2017 zookeeper
- -rw-r--r--. 1 root root 35042811 Nov 1 14:58 zookeeper-3.4.10.tar.gz
- ```
-
-4. 进入刚解压的 `zookeeper` 的目录中
-
- ```Shell
- [root@bogon software]# cd zookeeper/
- [root@bogon zookeeper]# ll
- total 1580
- drwxr-xr-x. 2 1001 1001 4096 Mar 23 2017 bin
- -rw-rw-r--. 1 1001 1001 84725 Mar 23 2017 build.xml
- drwxr-xr-x. 2 1001 1001 74 Mar 23 2017 conf
- drwxr-xr-x. 10 1001 1001 4096 Mar 23 2017 contrib
- drwxr-xr-x. 2 1001 1001 4096 Mar 23 2017 dist-maven
- drwxr-xr-x. 6 1001 1001 4096 Mar 23 2017 docs
- -rw-rw-r--. 1 1001 1001 1709 Mar 23 2017 ivysettings.xml
- -rw-rw-r--. 1 1001 1001 5691 Mar 23 2017 ivy.xml
- drwxr-xr-x. 4 1001 1001 4096 Mar 23 2017 lib
- -rw-rw-r--. 1 1001 1001 11938 Mar 23 2017 LICENSE.txt
- -rw-rw-r--. 1 1001 1001 3132 Mar 23 2017 NOTICE.txt
- -rw-rw-r--. 1 1001 1001 1770 Mar 23 2017 README_packaging.txt
- -rw-rw-r--. 1 1001 1001 1585 Mar 23 2017 README.txt
- drwxr-xr-x. 5 1001 1001 44 Mar 23 2017 recipes
- drwxr-xr-x. 8 1001 1001 4096 Mar 23 2017 src
- -rw-rw-r--. 1 1001 1001 1456729 Mar 23 2017 zookeeper-3.4.10.jar
- -rw-rw-r--. 1 1001 1001 819 Mar 23 2017 zookeeper-3.4.10.jar.asc
- -rw-rw-r--. 1 1001 1001 33 Mar 23 2017 zookeeper-3.4.10.jar.md5
- -rw-rw-r--. 1 1001 1001 41 Mar 23 2017 zookeeper-3.4.10.jar.sha1
- ```
- 解释一下,几个常见目录里的内容:
- - `bin` 目录里存放的是一些可执行文件,基本以 `*.bat` 和 `*.sh` 结尾的文件。
- - `conf` 目录里存放的是项目(软件)运行时的一些配置文件。
- - `contrib` 目录存放的内容官方未提供的一些附加软件。
- - `dist-maven` 目录存放的是 `zookeeper` 使用 `maven` 编译之后的源码和 jar文件
- - `docs` 目录里存放的是一些文档
- - `lib` 目录里存放的是依赖库
- - `recipes` 目录下存放的是使用 `zookeeper` 实现的分布式锁、高并发、队列的一些方案
- - `src` 目录里存放的是源码文件
-
-5. 在 `conf` 文件夹里对 `zookeeper` 进行配置,将示例配置文件复制并改名
-
- ```Shell
- [root@bogon software]# cd zookeeper/conf/
- [root@bogon conf]# ll
- total 12
- -rw-rw-r--. 1 1001 1001 535 Mar 23 2017 configuration.xsl
- -rw-rw-r--. 1 1001 1001 2161 Mar 23 2017 log4j.properties
- -rw-rw-r--. 1 1001 1001 922 Mar 23 2017 zoo_sample.cfg
- [root@bogon conf]# cp zoo_sample.cfg zoo.cfg
- [root@bogon conf]# ll
- total 16
- -rw-rw-r--. 1 1001 1001 535 Mar 23 2017 configuration.xsl
- -rw-rw-r--. 1 1001 1001 2161 Mar 23 2017 log4j.properties
- -rw-r--r--. 1 root root 922 Nov 2 14:40 zoo.cfg
- -rw-rw-r--. 1 1001 1001 922 Mar 23 2017 zoo_sample.cfg
- [root@bogon conf]# vim zoo.cfg
- ```
- 然后在 `zoo.cfg` 里加上一些配置
- ```bash
- tickTime=2000
- # The number of ticks that the initial
- # synchronization phase can take
- initLimit=10
- # The number of ticks that can pass between
- # sending a request and getting an acknowledgement
- syncLimit=5
- # the directory where the snapshot is stored.
- # do not use /tmp for storage, /tmp here is just
- # example sakes.
-
- ################ 修改的地方1 ################
- # dataDir=/tmp/zookeeper
- # 将原先的 dataDir 注释,并修改成自定义的路径
- dataDir=/software/zookeeper/data
-
- # the port at which the clients will connect
- clientPort=2181
- # the maximum number of client connections.
- # increase this if you need to handle more clients
- #maxClientCnxns=60
- #
- # Be sure to read the maintenance section of the
- # administrator guide before turning on autopurge.
- #
- # http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance
- #
- # The number of snapshots to retain in dataDir
- #autopurge.snapRetainCount=3
- # Purge task interval in hours
- # Set to "0" to disable auto purge feature
- #autopurge.purgeInterval=1
-
- ################ 修改的地方2 ################
- # 在文件的末尾加上
- server.0=172.16.15.129:2888:3888
- server.1=172.16.15.130:2888:3888
- server.2=172.16.15.131:2888:3888
- ```
-
-6. 新建配置文件中的 `dataDir` 配置的目录
-
- ```Shell
- [root@bogon conf]# cd ../
- [root@bogon zookeeper]# mkdir data
- [root@bogon zookeeper]# ll
- total 1580
- drwxr-xr-x. 2 1001 1001 4096 Mar 23 2017 bin
- -rw-rw-r--. 1 1001 1001 84725 Mar 23 2017 build.xml
- drwxr-xr-x. 2 1001 1001 88 Nov 2 14:57 conf
- drwxr-xr-x. 10 1001 1001 4096 Mar 23 2017 contrib
- drwxr-xr-x. 2 root root 6 Nov 2 14:58 data
- drwxr-xr-x. 2 1001 1001 4096 Mar 23 2017 dist-maven
- drwxr-xr-x. 6 1001 1001 4096 Mar 23 2017 docs
- -rw-rw-r--. 1 1001 1001 1709 Mar 23 2017 ivysettings.xml
- -rw-rw-r--. 1 1001 1001 5691 Mar 23 2017 ivy.xml
- drwxr-xr-x. 4 1001 1001 4096 Mar 23 2017 lib
- -rw-rw-r--. 1 1001 1001 11938 Mar 23 2017 LICENSE.txt
- -rw-rw-r--. 1 1001 1001 3132 Mar 23 2017 NOTICE.txt
- -rw-rw-r--. 1 1001 1001 1770 Mar 23 2017 README_packaging.txt
- -rw-rw-r--. 1 1001 1001 1585 Mar 23 2017 README.txt
- drwxr-xr-x. 5 1001 1001 44 Mar 23 2017 recipes
- drwxr-xr-x. 8 1001 1001 4096 Mar 23 2017 src
- -rw-rw-r--. 1 1001 1001 1456729 Mar 23 2017 zookeeper-3.4.10.jar
- -rw-rw-r--. 1 1001 1001 819 Mar 23 2017 zookeeper-3.4.10.jar.asc
- -rw-rw-r--. 1 1001 1001 33 Mar 23 2017 zookeeper-3.4.10.jar.md5
- -rw-rw-r--. 1 1001 1001 41 Mar 23 2017 zookeeper-3.4.10.jar.sha1
- ```
- 在 `data` 目录中新建一个 `myid` 文件,这个文件中的内容存放配置文件中对应 IP 的 `serverId`。
- ```Shell
- [root@bogon zookeeper]# cd data
- [root@bogon data]# vim myid
- [root@bogon data]# cat myid
- 0
- ```
-7. 将 `zookeeper` 的目录配置在 Linux 系统的环境变量中
-
- ```Shell
- [root@bogon data]# vim /etc/profile
- ```
- 在文件末尾添加
- ```bash
- export ZOOKEEPER_HOME=/software/zookeeper
- export PATH=$ZOOKEEPER_HOME/bin:$PATH
- ```
- 然后让环境变量生效
- ```Shell
- [root@bogon data]# source /etc/profile
- ```
-
-8. 将另外两台虚拟机也按照 `2-7` 的步骤配置,注意第6步的 `myid` 文件里存放的内容,应该与 `zoo.cfg` 配置文件末尾的 `server.n=ip:2888:3888` 的 `n` 对应
-9. 配置完环境变量之后,我们就可以不需要到 `bin` 目录下,直接在任意目录下执行 zookeeper 中 bin 目录下的文件了。
- - `zkServer.sh status` :查看 `zookeeper` 的状态
- - `zkServer.sh start` :启动 `zookeeper`
- - `zkServer.sh stop` :关闭 `zookeeper`
-
-10. 分别查看各个虚拟机的启动情况,以及启动后的状态。
-
- 虚拟机1:(follower)
- ```Shell
- [root@bogon software]# zkServer.sh status
- ZooKeeper JMX enabled by default
- Using config: /software/zookeeper/bin/../conf/zoo.cfg
- Error contacting service. It is probably not running.
- [root@bogon software]# zkServer.sh start
- ZooKeeper JMX enabled by default
- Using config: /software/zookeeper/bin/../conf/zoo.cfg
- Starting zookeeper ... STARTED
- [root@bogon software]# zkServer.sh status
- ZooKeeper JMX enabled by default
- Using config: /software/zookeeper/bin/../conf/zoo.cfg
- Mode: follower
- ```
- 虚拟机2:(leader)
- ```Shell
- [root@bogon ~]# zkServer.sh status
- ZooKeeper JMX enabled by default
- Using config: /software/zookeeper/bin/../conf/zoo.cfg
- Error contacting service. It is probably not running.
- [root@bogon ~]# zkServer.sh start
- ZooKeeper JMX enabled by default
- Using config: /software/zookeeper/bin/../conf/zoo.cfg
- Starting zookeeper ... STARTED
- [root@bogon ~]# zkServer.sh status
- ZooKeeper JMX enabled by default
- Using config: /software/zookeeper/bin/../conf/zoo.cfg
- Mode: leader
- ```
- 虚拟机3:(follower)
- ```Shell
- [root@bogon ~]# zkServer.sh status
- ZooKeeper JMX enabled by default
- Using config: /software/zookeeper/bin/../conf/zoo.cfg
- Error contacting service. It is probably not running.
- [root@bogon ~]# zkServer.sh start
- ZooKeeper JMX enabled by default
- Using config: /software/zookeeper/bin/../conf/zoo.cfg
- Starting zookeeper ... STARTED
- [root@bogon ~]# zkServer.sh status
- ZooKeeper JMX enabled by default
- Using config: /software/zookeeper/bin/../conf/zoo.cfg
- Mode: follower
- ```
-11. 至此,`zookeeper` 集群的简单搭建已经正式结束了。当然还有一些个性化(调优)的配置参数,请自行参照 `zookeeper` 的[官方文档](http://zookeeper.apache.org/doc/r3.4.10/zookeeperStarted.html) 在 `zoo.cfg` 中配置。
\ No newline at end of file
diff --git a/source/_posts/2017-12-05.redux-and-mobx-first.md b/source/_posts/2017-12-05.redux-and-mobx-first.md
deleted file mode 100644
index 87574caa..00000000
--- a/source/_posts/2017-12-05.redux-and-mobx-first.md
+++ /dev/null
@@ -1,72 +0,0 @@
----
-title: redux 和 mobx 初体验
-comments: true
-fancybox: false
-toc: false
-date: 2017-12-05 17:58:56
-tags:
-- react
-categories:
-- 技术相关
-- react
-description:
-permalink: redux-and-mobx-first
-top:
----
-前段时间搞了 Vue,现在又得开始写 React 了。 我堂堂的一个后端,真是太残忍啦 o(╯□╰)o
-
-接触过 Vue 的应该都知道,Vue 的状态管理可以有两种实现,一种是 Vuex,还有一种是 eventBus 形式。
-
-React 状态管理,老牌的应该是 Redux,但是存在了太多的概念,对于刚接触的童鞋不太友好。mobx 当属时代的新星了。不管从效率,还是易读性都远超 Redux。
-
-下面带来俩小🌰大家感受下~~
-
-
-
-#### 最简单的一个 redux 操作
-
-```js
-import {createStore} from 'redux';
-
-const ADD_ACTION = 'ADD';
-
-const add = (num) => {
- return {
- type: ADD_ACTION,
- num
- };
-};
-
-const initialState = {
- count: 0
-};
-
-const reducers = (state = initialState, action) => {
- switch (action.type) {
- case ADD_ACTION:
- return Object.assign({}, state, {count: state.count + action.num});
- default:
- return state;
- }
-};
-
-const reduxStore = createStore(reducers);
-
-reduxStore.dispatch(add(1));
-```
-
-#### 最简单的一个 mobx 操作
-
-```js
-import {observable, action} from 'mobx';
-
-const mobxStore = observable({
- count: 0,
- add: action((num) => {
- this.count += num;
- })
-});
-
-mobxStore.add(1);
-```
-
diff --git a/source/_posts/2018-01-04.devops-jenkins.md b/source/_posts/2018-01-04.devops-jenkins.md
deleted file mode 100644
index 9612ab8f..00000000
--- a/source/_posts/2018-01-04.devops-jenkins.md
+++ /dev/null
@@ -1,215 +0,0 @@
----
-title: DevOps 之 Jenkins 安装、配置、美化、插件及常见错误处理
-comments: true
-fancybox: true
-toc: true
-date: 2018-01-04 13:19:13
-tags:
-- jenkins
-- devops
-- ci
-categories:
-- 技术相关
-- devops
-description:
-permalink: devops-jenkins
-top:
----
-继续上一篇的话题,既然已经搭建了 GitLab 的代码仓库,那么现在就可以开始进行下一步持续集成环境的搭建了。公司准备利用 Jenkins CI 进行持续集成,本文记录了 Jenkins 的安装、基础配置、界面美化、常用插件及常见错误处理。
-
-
-
-## 1. 安装Jenkins
-
-**准备工作**
-
-1. 安装 JDK,并配置环境变量
-
-2. 安装 maven
-
-3. 配置阿里云的 maven 镜像源地址,`vim /usr/share/maven/conf/settings.xml` 将👇内容添加进 `mirrors` 节点
-
- ```xml
-
-
- nexus-aliyun
- *
- Nexus aliyun
- http://maven.aliyun.com/nexus/content/groups/public
-
- ```
-
-4. 安装 git
-
-**配置镜像源地址**
-
-```shell
-sudo wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat-stable/jenkins.repo
-sudo rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io.key
-yum install jenkins
-```
-
-**修改访问 IP 和 端口号**
-
-1. `vim /etc/sysconfig/jenkins`
-2. `JENKINS_PORT="10080"`
-
-**配置 JDK 路径到 Jenkins 中**
-
-1. `vim /etc/init.d/jenkins`
-
-2. 将自己的 java 目录加入到 `candidates` 里
-
- ```properties
- candidates="
- /etc/alternatives/java
- /usr/lib/jvm/java-1.8.0/bin/java
- /usr/lib/jvm/jre-1.8.0/bin/java
- /usr/lib/jvm/java-1.7.0/bin/java
- /usr/lib/jvm/jre-1.7.0/bin/java
- /usr/bin/java
- /opt/jdk1.8.0_144/bin/java
- "
- ```
-
-**安装完毕后启动**
-
-```shell
-sudo service jenkins start # 启动jenkins服务
-```
-
-## 2. 配置 Jenkins
-
-1. **初始化密码**
-
- `cat /var/lib/jenkins/secrets/initialAdminPassword`
-
-2. **安装插件**
-
- 选择左侧推荐的插件安装即可
-
-3. **配置管理员账号密码**
-
-4. **配置 JDK、Maven、Git、Docker**
-
- 系统管理 → 全局工具配置(尽量在服务器本地安装,然后输入安装路径,不推荐使用在线安装,因为在线安装有些资源都在国外,下载速度极慢)
-
-5. **配置邮件服务器信息**
-
- 系统管理 → 系统设置,①Jenkins Location→系统管理员邮件地址②Extended E-mail Notification③邮件通知。
-
- 其中**Extended E-mail Notification**的两个地方可以做个性化配置:
-
- - Default Content Type 邮件的内容类型,改为`HTML(text/html)`。
-
- - Default Subject 邮件的标题,改为`构建通知:$PROJECT_NAME - Build # $BUILD_NUMBER - $BUILD_STATUS!`。
-
- - Default Content 邮件的内容,改为👇内容。
-
- ```html
-
-
-
-
- ${PROJECT_NAME}-第${BUILD_NUMBER}次构建日志
-
-
-
-
-
- (本邮件是程序自动下发的,请勿回复!)
-
-
-
- 构建结果 - ${BUILD_STATUS}
-
-
-
-
- 构建信息
-
-
-
-
-
-
-
-
- Changes Since Last Successful Build:
-
-
-
-
- ${CHANGES_SINCE_LAST_SUCCESS,reverse=true, format="Changes for Build #%n: %c ",showPaths=true,changesFormat="[%a] %m ",pathFormat=" %p"}
-
-
-
- Test Informations
-
-
-
- Total:${TEST_COUNTS,var="total"},Pass:${TEST_COUNTS,var="pass"},Failed:${TEST_COUNTS,var="fail"},Skiped:${TEST_COUNTS,var="skip"}
-
-
-
- 构建日志 (最后 100行):
-
-
-
-
-
-
-
-
-
- ```
-
- - 勾选上 **Enable Debug Mode、Require Administrator for Template Testing、Enable watching for jobs、Allow sending to unregistered users**
-
-## 3. 美化 Jenkins
-
-安利一款material风格的主题:http://afonsof.com/jenkins-material-theme/ ,支持使用 CDN,也支持自定义公司 logo 等等。
-
-## 4. 常用插件
-
-1. Maven Integration plugin:用于构建 Maven 项目
-2. Gitlab Hook Plugin:用于触发 GitLab 的一些 WebHooks 来构建项目
-3. Simple Theme Plugin:配合上面的美化 Jenkins 使用
-4. FireLine Plugin:代码规范检查,已经集成了阿里巴巴的代码规约(P3C)检查
-5. HTML Publisher plugin:将一些测试结果变成 HTML 文档
-6. JUnit Plugin:JUnit 测试
-7. [Checkstyle Plug-in](https://plugins.jenkins.io/checkstyle):检测代码风格
-8. [FindBugs Plug-in](https://plugins.jenkins.io/findbugs):检测构建出来的 class 文件是否安全
-9. [Static Analysis Collector Plug-in](https://plugins.jenkins.io/analysis-collector):对7、8两个插件检测出来的结果进行汇总
-10. Extended E-mail Notification:个性化邮件模板
-11. [Dingding[钉钉] Plugin](https://wiki.jenkins.io/display/JENKINS/Dingding+Notification+Plugin):钉钉通知插件
-
-## 5. 常见错误解决
-
-**添加镜像源的时候出现如下错误:**
-
-```shell
-Resolving pkg.jenkins-ci.org (pkg.jenkins-ci.org)... failed: Temporary failure in name resolution.
-
-wget: unable to resolve host address ‘pkg.jenkins-ci.org’
-```
-
-解决方法:
-
-`vim /etc/resolv.conf` 添加一行 `nameserver 8.8.8.8`
-
-**HTML Publisher Plugin 插件在新的Jenkins版本中,打开生成的网页中无法加载CSS、无法点击按钮等情况:**
-
-由于 CSP(Content Security Policy)所致,可在 系统管理→脚本命令行 中执行:
-
- `System.setProperty("hudson.model.DirectoryBrowserSupport.CSP","")`
\ No newline at end of file
diff --git a/source/_posts/2018-02-05.osx-software-lists.md b/source/_posts/2018-02-05.osx-software-lists.md
deleted file mode 100644
index 4567dbc4..00000000
--- a/source/_posts/2018-02-05.osx-software-lists.md
+++ /dev/null
@@ -1,131 +0,0 @@
----
-title: OSX 常用软件汇总
-comments: true
-fancybox: false
-toc: true
-date: 2018-02-05 13:41:03
-tags:
-- 软件
-- Mac
-categories:
-- 系统
-description:
-permalink: osx-software-lists
-top:
----
-每次重装系统,或者新电脑到手的时候,最麻烦的莫过于重新安装之前常用的一些软件了。但是,平时常用的软件实在有点多,于是乎,整理一个 OSX 常用软件的列表,便于以后查找使用,以下整理的软件仅代表我个人平时常用的软件,不喜勿喷。
-
-
-
-最喜欢的一个「免(破)费(解)」的 osx 软件下载网址:[xclient.info](http://xclient.info/) ,有经济能力的童鞋还是请支持正版。。。
-
-## 聊天类软件
-
-| 软件 | 介绍及下载地址 |
-| ---- | ---------------------------------------- |
-| QQ | 略,http://im.qq.com/macqq/ |
-| 微信 | 略,Mac App Store |
-| 阿里旺旺 | 淘宝买家与卖家之间的沟通工具,https://alimarket.taobao.com/markets/qnww/portal-group/ww/download |
-| 钉钉 | 智能移动办公平台,https://www.dingtalk.com/ |
-
-## 影音类软件
-
-| 软件 | 介绍及下载地址 |
-| ------ | ---------------------------------------- |
-| 网易云音乐 | 网易一款良心之作,听歌很棒,听歌看评论更棒,https://music.163.com/#/download |
-| Movist | 对比过很多视频播放器,最终这款我比较喜欢,http://xclient.info/s/movist.html?_=80fb58a8555e8bf529af34d2f81df569 |
-
-## 笔记类软件
-
-| 软件 | 介绍及下载地址 |
-| -------- | ---------------------------------------- |
-| 有道云笔记 | 略,http://note.youdao.com/download.html#mac |
-| Evernote | 略,https://www.yinxiang.com/download/ |
-| MWeb | 一款 `markdown` 编辑器,比较喜欢的是可以将图片上传至七牛云等地方,适合做笔记,http://xclient.info/s/mweb.html?_=80fb58a8555e8bf529af34d2f81df569 |
-| Typora | 和 MWeb 一样,是一款 `markdown` 编辑器,本文就是用这个软件写的,所见即所得,适合写博客文章,https://www.typora.io/ |
-
-## 摄影类软件
-
-| 软件 | 介绍及下载地址 |
-| ------------------ | ---------------------------------------- |
-| 泼辣修图 | 国人开发的一款好用的修图软件,包含及其丰富的滤镜,http://xclient.info/s/polarr-photo-editor.html?_=80fb58a8555e8bf529af34d2f81df569 |
-| Snapseed | 一款国内外知名的修图软件,需要一定的操作,Mac App Store |
-| Super PhotoCut Pro | 超级抠图,http://xclient.info/s/super-photocut.html?_=80fb58a8555e8bf529af34d2f81df569 |
-| Photoshop | 这个就不介绍了,摄影、修图必备,http://xclient.info/s/adobe-photoshop-cc.html?_=80fb58a8555e8bf529af34d2f81df569 |
-| Lightroom | 对 RAW 格式的照片做处理,http://xclient.info/s/adobe-lightroom-cc.html?_=80fb58a8555e8bf529af34d2f81df569 |
-
-## 效率类软件
-
-| 软件 | 介绍及下载地址 |
-| -------------------- | ---------------------------------------- |
-| Alfred | 效率神器,可以不用鼠标,召唤出你想要的一切,http://xclient.info/s/alfred.html?_=80fb58a8555e8bf529af34d2f81df569 |
-| Clean My Mac 3 | Mac 的一款包含磁盘垃圾清理及硬件资源监控等功能的软件,http://www.mycleanmymac.com/ |
-| CheetSheat | 长按⌘键,展示当前窗口应用的快捷键,https://www.mediaatelier.com/CheatSheet/ |
-| Dash | 开发者必备 API文档聚合,http://xclient.info/s/dash.html?_=80fb58a8555e8bf529af34d2f81df569 |
-| MindNote | 思维导图工具,http://xclient.info/s/mind-node-pro.html?_=80fb58a8555e8bf529af34d2f81df569 |
-| XMind | 思维导图工具,https://www.xmind.cn/download/ |
-| iTerm | 替换自带的 Terminal 程序,更好用,http://www.iterm2.com/ |
-| Moom | 强大的窗口布局工具,http://xclient.info/s/moom.html?_=80fb58a8555e8bf529af34d2f81df569 |
-| Microsoft PowerPoint | PPT,Microsoft Office 2016 for Mac 套件中,http://xclient.info/s/office-for-mac-2016.html?_=80fb58a8555e8bf529af34d2f81df569 |
-| Microsoft Excel | Excel,Microsoft Office 2016 for Mac 套件中,同上 |
-| Microsoft Word | Word,Microsoft Office 2016 for Mac 套件中,同上 |
-| Microsoft OneNote | OneNote,Microsoft Office 2016 for Mac 套件中,同上 |
-| Pages | 一款文字处理和页面排版工具,类似 Word,Mac App Store |
-| Keynote | 运行于Mac OS X操作系统下的演示幻灯片应用软件,类似 PPT,Mac App Store |
-| Numbers | 一款电子表单应用程序,类似 Excel,Mac App Store |
-| OmniPlan | 最NB的项目管理流程软件,http://xclient.info/s/omni-plan.html?_=80fb58a8555e8bf529af34d2f81df569 |
-| OmniGraffle | 强大的图形工具,可以用来绘制图表,流程图,组织结构图以及插图,http://xclient.info/s/omnigraffle.html?_=80fb58a8555e8bf529af34d2f81df569 |
-
-## 实用工具类软件
-
-| 软件 | 介绍及下载地址 |
-| ----------------------------- | ---------------------------------------- |
-| NewFileMenu | 可以在 Mac 上使用右键创建文档,http://xclient.info/s/new-file-menu.html?_=80fb58a8555e8bf529af34d2f81df569 |
-| ScreenFlow | 一款优秀的屏幕录像软件,http://xclient.info/s/screenflow.html?_=80fb58a8555e8bf529af34d2f81df569 |
-| 百度网盘 | 提供文件的网络备份、同步和分享服务,https://pan.baidu.com/download#pan |
-| FlashFTP | 一款高效的FTP、SFTP文件传输客户端,Mac App Store |
-| 迅雷 | 下载工具,http://mac.xunlei.com/ |
-| SpechtLite | 科学上网工具,需配合「[熊猫翻滚](https://ezssl.net/?r=109610)」使用,https://upcloud-pic.b0.upaiyun.com/panda/spechlite/0.10.7.zip |
-| PDF Expert | 一款可以对 PDF 进行标注的阅读器,Mac App Store |
-| Rar Sharp | rar文件解压工具,Mac App Store |
-| Things 3 | 一款优秀的 GTD 任务管理工具,http://xclient.info/s/things.html?_=80fb58a8555e8bf529af34d2f81df569 |
-| VMware Fusion | 强大的虚拟机应用,http://xclient.info/s/vmware-fusion.html?_=80fb58a8555e8bf529af34d2f81df569 |
-| Gemini 2 | 高效的重复文件查找器,http://xclient.info/s/gemini.html?_=80fb58a8555e8bf529af34d2f81df569 |
-| EdrawMax | 基于矢量的绘图工具,轻松绘制流程图 |
-| PP助手 | 可以将文件传给 iPhone,不喜欢 iTunes,https://pro.25pp.com/pp_mac_ios |
-| Charles | 抓包工具,http://charles.iiilab.com/ |
-| VirtualBox | 免费的虚拟机软件,https://www.virtualbox.org/ |
-| duet | 可以将 mac 屏幕投影到 iPhone 或者 ipad 上,将其作为第二屏,https://www.duetdisplay.com/ |
-| Lantern | 免费的科学上网神器,https://github.com/getlantern/lantern/releases/tag/latest |
-| TeamViewer | 远程支持、远程访问、在线协作和会议,https://www.teamviewer.com/zhcn/download/mac/ |
-| Microsoft Remote Desktop Beta | 支持让 Mac 远程连接到 windows 服务器,https://rink.hockeyapp.net/apps/5e0c144289a51fca2d3bfa39ce7f2b06/ |
-| Xee3 | 支持多格式的图片浏览工具,http://xclient.info/s/xee3.html?_=80fb58a8555e8bf529af34d2f81df569 |
-| Tuxera Disk Manager | 让你的 Mac 支持读取 NTFS 格式的硬盘 |
-| iStatistica | 一款高颜值的系统监控工具,http://xclient.info/s/istatistica.html?_=80fb58a8555e8bf529af34d2f81df569 |
-| TinyCal | 一款小而美的日历,可以放置在通知栏上,支持阴历,http://xclient.info/s/tinycal.html?_=80fb58a8555e8bf529af34d2f81df569 |
-
-## 开发类软件
-
-| 软件 | 介绍及下载地址 |
-| ------------------- | ------------------------------------------------------------ |
-| IntelliJ IDEA | Java 主力开发软件,http://www.jetbrains.com/idea/ |
-| WebStorm | 前端开发,http://www.jetbrains.com/webstorm/ |
-| PyCharm | Python 开发,http://www.jetbrains.com/pycharm/ |
-| Android Studio | Android 开发,https://developer.android.com/studio/index.html |
-| Xcode | IOS 开发,Mac App Store |
-| HBuilder | 跨平台的 mui 应用开发,http://www.dcloud.io/ |
-| Sublime text | 文本编辑器,https://www.sublimetext.com/ |
-| Virtual Studio Code | 文本编辑器,https://code.visualstudio.com/download |
-| Navicat Premium | 一个可多重连接的数据库管理工具,支持的数据库有: MySQL、SQL Server、SQLite、Oracle 及 PostgreSQL,http://xclient.info/s/navicat-premium.html?_=80fb58a8555e8bf529af34d2f81df569 |
-| JD-GUI | 反编译 class 文件,http://jd.benow.ca/ |
-| docker | 容器,https://www.docker.com/get-docker |
-| rdm | redis 桌面管理软件,https://github.com/uglide/RedisDesktopManager |
-| Genymotion | 安卓模拟器,https://www.genymotion.com/ |
-| Beyond Compare | 文件比对软件 |
-| Mock Plus | 原型图软件,https://www.mockplus.cn/ |
-| Sketch | 移动应用矢量绘图设计工具,http://xclient.info/s/sketch.html?_=80fb58a8555e8bf529af34d2f81df569 |
-| Axure RP 8 | 快速原型设计工具,http://xclient.info/s/axure-rp.html?_=80fb58a8555e8bf529af34d2f81df569 |
-| Postman | HTTP 模拟请求工具,https://www.getpostman.com/ |
-| Insomnia | HTTP 模拟请求工具,比 Postman 界面更干净,https://insomnia.rest/ |
-| SSH Shell | 远程连接到 Linux 服务器,管理多个 SSH 会话,http://xclient.info/s/ssh-shell.html?_=80fb58a8555e8bf529af34d2f81df569 |
-| LaunchRocket | 统一管理 Mac 上的一些服务,比如说 redis、mysql、zookeeper、rabbitmq等等服务,https://github.com/jimbojsb/launchrocket |
\ No newline at end of file
diff --git a/source/_posts/2018-09-18.how-to-update-the-fork-project.md b/source/_posts/2018-09-18.how-to-update-the-fork-project.md
deleted file mode 100644
index de6164a7..00000000
--- a/source/_posts/2018-09-18.how-to-update-the-fork-project.md
+++ /dev/null
@@ -1,82 +0,0 @@
----
-title: git 如何更新 fork 的项目到原项目的最新版本
-comments: true
-fancybox: false
-toc: false
-date: 2018-09-18 09:04:23
-tags:
-- git
-- GitHub
-categories:
-- 技术相关
-- git
-description:
-permalink: how-to-update-the-fork-project
-top:
----
-
-在 github 上 fork 了一个项目之后,如何使自己 fork 的项目和原先作者的项目分支保持同步呢,下面我使用 hutool 的项目做示范,手摸手示范如何使 fork 的项目与原项目分支保持同步。
-
-
-
-1. **查看远程的版本库地址**
-
- ```bash
- $ git remote -v
- origin https://github.com/xkcoding/hutool.git (fetch)
- origin https://github.com/xkcoding/hutool.git (push)
- ```
-
-2. **添加原项目 git 地址到本地版本库**
-
- ```bash
- $ git remote add upstream https://github.com/looly/hutool.git
- ```
-
-3. **检查版本库是否添加成功**
-
- ```bash
- $ git remote -v
- origin https://github.com/xkcoding/hutool.git (fetch)
- origin https://github.com/xkcoding/hutool.git (push)
- upstream https://github.com/looly/hutool.git (fetch)
- upstream https://github.com/looly/hutool.git (push)
- ```
-
-4. **原项目更新内容同步到本地**
-
- ```bash
- $ git fetch upstream
- remote: Counting objects: 360, done.
- remote: Compressing objects: 100% (148/148), done.
- remote: Total 360 (delta 158), reused 339 (delta 137), pack-reused 0
- Receiving objects: 100% (360/360), 87.95 KiB | 45.00 KiB/s, done.
- Resolving deltas: 100% (158/158), completed with 66 local objects.
- From https://github.com/looly/hutool
- * [new branch] v4-dev -> upstream/v4-dev
- * [new branch] v4-master -> upstream/v4-master
- ```
-
-5. **查看本地分支**
-
- ```bash
- $ git branch -a
- * v4-master
- remotes/origin/HEAD -> origin/v4-master
- remotes/origin/v4-dev
- remotes/origin/v4-master
- remotes/upstream/v4-dev
- remotes/upstream/v4-master
- ```
-
-6. **同步更新内容到本地对应分支**
-
- ```bash
- $ git merge upstream/v4-master
- ```
-
-7. **提交更新内容到 fork 地址**
-
- ```bash
- git push
- ```
diff --git a/source/_posts/2019-01-17.solve-nginx-413-problem.md b/source/_posts/2019-01-17.solve-nginx-413-problem.md
deleted file mode 100644
index 3024c553..00000000
--- a/source/_posts/2019-01-17.solve-nginx-413-problem.md
+++ /dev/null
@@ -1,34 +0,0 @@
----
-title: 解决 Nginx 413 Request Entity Too Large 问题
-comments: true
-fancybox: false
-toc: true
-date: 2019-01-17 10:44:21
-tags:
-- nginx
-categories:
-- 技术相关
-- nginx
-description:
-permalink: solve-nginx-413-problem
-top:
----
-使用 Spring Boot 文件上传的时候,前端使用 nginx 代理后端请求,出现如下 **413 Request Entity Too Large** 这个错误。
-
-
-
-
-
-解决方法其实也十分简单,只需要在 nginx 配置文件里添加如下内容,重启 nginx,即可解决。
-
-```nginx
-server {
- # ...
- listen 80;
- server_name localhost;
- client_max_body_size 20M;
- # ...
-}
-```
-
-重启 nginx,`nginx -s reload`,大功告成!
\ No newline at end of file
diff --git a/source/_posts/2020-09-13.what-i-like-to-do-0.md b/source/_posts/2020-09-13.what-i-like-to-do-0.md
deleted file mode 100644
index 830b1bea..00000000
--- a/source/_posts/2020-09-13.what-i-like-to-do-0.md
+++ /dev/null
@@ -1,23 +0,0 @@
----
-title: 周末的不务正业~
-comments: true
-fancybox: false
-toc: true
-date: 2020-09-13 15:09:22
-tags:
-- 忙里偷闲
-categories:
-- 忙里偷闲
-description:
-permalink: what-i-like-to-do-0
-top:
----
-周末的不务正业~
-
-花了一个半小时折的燕子
-
-
-
-
-
-
\ No newline at end of file
diff --git a/source/_posts/2021-05-11.happy-dev-traefik-proxy.md.bak b/source/_posts/2021-05-11.happy-dev-traefik-proxy.md.bak
deleted file mode 100644
index 3841af0b..00000000
--- a/source/_posts/2021-05-11.happy-dev-traefik-proxy.md.bak
+++ /dev/null
@@ -1,22 +0,0 @@
----
-title: 愉快的本地开发体验之Traefik代理
-comments: true
-fancybox: false
-toc: true
-permalink: happy-dev-traefik-proxy
-date: 2021-05-11 18:42:11
-tags:
-- docker
-- traefik
-categories:
-- 技术相关
-- docker
-description:
-top:
----
-## 前言
-
-
-
-博文渲染失败,o(╥﹏╥)o 麻烦看到小伙伴,联系下作者,谢谢啦~
-
diff --git a/source/_posts/2021-05-20.happy-dev-yapi.md.bak b/source/_posts/2021-05-20.happy-dev-yapi.md.bak
deleted file mode 100644
index ac326f3d..00000000
--- a/source/_posts/2021-05-20.happy-dev-yapi.md.bak
+++ /dev/null
@@ -1,22 +0,0 @@
----
-title: 愉快的本地开发体验之YAPI接口文档管理
-comments: true
-fancybox: false
-toc: true
-permalink: happy-dev-yapi
-date: 2021-05-20 10:02:11
-tags:
-- docker
-- yapi
-categories:
-- 技术相关
-- docker
-description:
-top:
----
-## 前言
-
-
-
-博文渲染失败,o(╥﹏╥)o 麻烦看到小伙伴,联系下作者,谢谢啦~
-
diff --git a/source/about/index.md b/source/about/index.md
deleted file mode 100644
index ef053943..00000000
--- a/source/about/index.md
+++ /dev/null
@@ -1,61 +0,0 @@
----
-title: 关于
-date: 2020-01-09 00:00:00
-fancybox: false
-toc: false
----
-不积跬步无以至千里,不积小流无以成江海。
-《劝学》—— 荀子
-
-## 关于本站
-
- 这个话题,我回忆了好久,思绪开始随着时间线不断地往前推移。
-2013 年,我刚上大学,记得那会儿大一在上 C 以及高数,由于我乐于助(zhuang)人(bi)的优秀品质,我在 QQ 空间分享一些考试重难点及上课笔记,帮助同学考前复习。想来这大概是我博客的原型。
-
-后面随着软件学习的不断深入,QQ 空间的自身特性,不利于技术文章的编写与沉淀,我转向了 [Lofter](http://www.lofter.com/) ,以至于到今年(2019)年初,在搜索引擎搜索「`xkcoding`」的时候,排在前几位的还是 LOFTER 的文章,于是,我狠心的将其注销了~
-
-写了一段时间的 LOFTER 之后,我又转向了国内的几个大型同性交友网站,CSDN、博客园、开源中国,也都去尝试过一段时间,最终还是没能坚持下来。
-
-大二那段时间,应该是 `WordPress` 开始流行的那段时间,我也紧跟潮流的玩了起来,拿着不多的生活费,在一些无良淘宝卖家购买「虚机」,兴致勃勃的搭建「`LAMP`」环境、搭建「`MySQL`」环境,在网上废寝忘食的寻找 `WordPress` 的主题及插件,用的最舒服的还是「`老赵茶馆`」系列的主题,这也是我唯一一个购买的主题。无奈,站点最终没能活下来,永远都忘不了那些无良的虚机以及一堆涉黄的垃圾评论。
-
-想想还是怀念大二那段时间,在自己未知的世界摸索前行,没有失败的气馁,有的只是成功的喜悦。本站的域名「`xkcoding.com`」还是那段时间注册的,也是在那段时间备案的。[前往查看whois 👉](https://whois.aliyun.com/whois/domain/xkcoding.com)
-
-后来大三了,随着视野的拓宽,开始逐渐了解到静态博客网站,然后便开始了「`Jekyll + GitHub Pages`」的组合拳,感受就是,原来写博客的体验这么轻松加愉快,markdown 简直就是文档编辑利器!后面因为 GitHub 国内的访问速度太慢,于是又折腾了「`Jekyll + GitHub Pages + Coding.net`」的搭配,在 DNS 解析的时候,国内解析到 GitHub,国内解析到 Coding,「`Coding.net `」现在也已经改名叫做「`腾讯云开发平台`」了。
-
-再到后面,就是 Hexo 了,本站使用的就是「Hexo + Next 主题」,主要还是喜欢 Hexo 各式各样的主题,好在 Jekyll 迁移到 Hexo 几乎不费任何成本。
-
-hexo 的折腾丝毫不少,比如:
-
-- 评论,多说退出历史舞台,导致找了很久评论的替代品,最终还是选择了 `Disqus`
-- 16 年更换了 Mac,window 和 Mac 的 Hexo 环境迁移
-- hexo 的搜索
-- 部署在自己服务器,同时也折腾了 CI 和 CD,享受写文章的乐趣,其余的交给机器搞定
-- 容器化部署
-- ......
-
-最后,也尝试过 [基于GitBook搭建的面经](https://docs.xkcoding.com/) 和 [基于VuePress搭建的笔记](https://note.xkcoding.com/) 以及语雀的文档编辑,「路漫漫其修远兮,吾将上下而求索」~
-
-## 关于 xkcoding
-
- 我叫「`沈扬凯`」,大学的兄嘚们喊我「`小凯`」,代码里的注释我会写「`Yangkai.Shen`」,网络上我叫「`xkcoding`」,而我是个 Java 开发工程师,所以翻译过来就是「`小凯编程`」。
-
-我热爱游戏,但我是个黑铁玩家,热爱编程,却不敢尝试大厂,热爱[开源](https://github.com/xkcoding),也才有点成绩,不过爱[妞妞](https://love.xkcoding.com),却改变了我的整个世界!
-
-我 17 年毕业,「湖北文理学院」,是「二本」,学的是「物联网工程」专业,16 年大三下,前往北京参加工作,「图迹科技」是我入职的第一家公司,认识到一帮相交甚好的朋友,感恩!
-
-18 年年初,回到杭州,现在「杭州」打杂,欢迎约饭、畅聊!
-
-「不积跬步无以至千里,不积小流无以成江海」这是我的座右铭,我始终坚信这个道理,相信在不久的将来,一定会迎来「厚积薄发」!
-
-
-## 联系 xkcoding
-
-
-
-- [EMail](mailto:237497819@qq.com)
-- [GitHub](https://github.com/xkcoding/)
-- [QQ](http://wpa.qq.com/msgrd?v=3&uin=237497819&site=qq&menu=yes)
-- 查看我的完整简历
-
-送你们一首李荣浩 的《麻雀》
-
diff --git a/source/categories/index.md b/source/categories/index.md
deleted file mode 100644
index 6dbb3d72..00000000
--- a/source/categories/index.md
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: categories
-date: 2016-12-15 10:28:36
-type: "categories"
-comments: false
----
diff --git a/source/favicon.png b/source/favicon.png
deleted file mode 100644
index 22493711..00000000
Binary files a/source/favicon.png and /dev/null differ
diff --git a/source/images/avatar.png b/source/images/avatar.png
deleted file mode 100644
index 22493711..00000000
Binary files a/source/images/avatar.png and /dev/null differ
diff --git a/source/images/favicon.png b/source/images/favicon.png
deleted file mode 100644
index 22493711..00000000
Binary files a/source/images/favicon.png and /dev/null differ
diff --git a/source/images/qrcode_for_xkcoding.jpg b/source/images/qrcode_for_xkcoding.jpg
deleted file mode 100644
index d825bd1a..00000000
Binary files a/source/images/qrcode_for_xkcoding.jpg and /dev/null differ
diff --git a/source/links/index.md b/source/links/index.md
deleted file mode 100644
index 3400d1ca..00000000
--- a/source/links/index.md
+++ /dev/null
@@ -1,9 +0,0 @@
----
-title: 友链
-date: 2020-04-02 11:44:01
-type: "links"
----
-留言交换友链,同时添加本站地址
-
-> 复制添加本站链接:`Coding Diary `
-> 复制添加本站图标:`https://static.xkcoding.com/blog/2020-04-02-xkcoding_favicon.png`
diff --git a/source/resources/alipay-reward-image.JPG b/source/resources/alipay-reward-image.JPG
deleted file mode 100644
index 83b3ab7a..00000000
Binary files a/source/resources/alipay-reward-image.JPG and /dev/null differ
diff --git a/source/resources/alipay-reward-image.png b/source/resources/alipay-reward-image.png
deleted file mode 100644
index a2fb5062..00000000
Binary files a/source/resources/alipay-reward-image.png and /dev/null differ
diff --git a/source/resources/alipay-reward-image1.png b/source/resources/alipay-reward-image1.png
deleted file mode 100644
index 4f3f2b81..00000000
Binary files a/source/resources/alipay-reward-image1.png and /dev/null differ
diff --git a/source/resources/alipay-reward-image2.png b/source/resources/alipay-reward-image2.png
deleted file mode 100644
index c5c78170..00000000
Binary files a/source/resources/alipay-reward-image2.png and /dev/null differ
diff --git a/source/resources/android-svn-attention-20160403130946.png b/source/resources/android-svn-attention-20160403130946.png
deleted file mode 100644
index 763b72bb..00000000
Binary files a/source/resources/android-svn-attention-20160403130946.png and /dev/null differ
diff --git a/source/resources/coding-standards-1.png b/source/resources/coding-standards-1.png
deleted file mode 100644
index 040a7e45..00000000
Binary files a/source/resources/coding-standards-1.png and /dev/null differ
diff --git a/source/resources/coding-standards-2.1.png b/source/resources/coding-standards-2.1.png
deleted file mode 100644
index c5126c99..00000000
Binary files a/source/resources/coding-standards-2.1.png and /dev/null differ
diff --git a/source/resources/coding-standards-2.2.png b/source/resources/coding-standards-2.2.png
deleted file mode 100644
index 6468366d..00000000
Binary files a/source/resources/coding-standards-2.2.png and /dev/null differ
diff --git a/source/resources/coding-standards-3.png b/source/resources/coding-standards-3.png
deleted file mode 100644
index 0cb029d6..00000000
Binary files a/source/resources/coding-standards-3.png and /dev/null differ
diff --git a/source/resources/eclipse-tips-20151107221921217.png b/source/resources/eclipse-tips-20151107221921217.png
deleted file mode 100644
index cf3de049..00000000
Binary files a/source/resources/eclipse-tips-20151107221921217.png and /dev/null differ
diff --git a/source/resources/greenvpn.gif b/source/resources/greenvpn.gif
deleted file mode 100644
index 782fae6a..00000000
Binary files a/source/resources/greenvpn.gif and /dev/null differ
diff --git a/source/resources/iov-1.png b/source/resources/iov-1.png
deleted file mode 100644
index c56170cc..00000000
Binary files a/source/resources/iov-1.png and /dev/null differ
diff --git a/source/resources/iov-2.png b/source/resources/iov-2.png
deleted file mode 100644
index 6b681ff6..00000000
Binary files a/source/resources/iov-2.png and /dev/null differ
diff --git a/source/resources/iov-3.png b/source/resources/iov-3.png
deleted file mode 100644
index 97fb7c68..00000000
Binary files a/source/resources/iov-3.png and /dev/null differ
diff --git a/source/resources/iov-4.png b/source/resources/iov-4.png
deleted file mode 100644
index f852759b..00000000
Binary files a/source/resources/iov-4.png and /dev/null differ
diff --git a/source/resources/iov-5.png b/source/resources/iov-5.png
deleted file mode 100644
index 068b7f88..00000000
Binary files a/source/resources/iov-5.png and /dev/null differ
diff --git a/source/resources/volley-demo-20160326012642.png b/source/resources/volley-demo-20160326012642.png
deleted file mode 100644
index 3c338a8b..00000000
Binary files a/source/resources/volley-demo-20160326012642.png and /dev/null differ
diff --git a/source/resources/wechat-reward-image.JPG b/source/resources/wechat-reward-image.JPG
deleted file mode 100644
index 0c908626..00000000
Binary files a/source/resources/wechat-reward-image.JPG and /dev/null differ
diff --git a/source/resources/wechat-reward-image.png b/source/resources/wechat-reward-image.png
deleted file mode 100644
index 24c95789..00000000
Binary files a/source/resources/wechat-reward-image.png and /dev/null differ
diff --git a/source/resources/wechat-reward-image1.png b/source/resources/wechat-reward-image1.png
deleted file mode 100644
index e643e144..00000000
Binary files a/source/resources/wechat-reward-image1.png and /dev/null differ
diff --git a/source/resources/wechat-reward-image2.png b/source/resources/wechat-reward-image2.png
deleted file mode 100644
index 6ef52bfe..00000000
Binary files a/source/resources/wechat-reward-image2.png and /dev/null differ
diff --git a/source/resume.md b/source/resume.md
deleted file mode 100644
index e5bd8824..00000000
--- a/source/resume.md
+++ /dev/null
@@ -1,8 +0,0 @@
----
-title: 简历
-comments: false
-date: 2019-07-01 09:44:41
-permalink: resume
----
-{% pdf https://static.aliyun.xkcoding.com/resume/2022/10/01/Java%E5%BC%80%E5%8F%91_%E6%B2%88%E6%89%AC%E5%87%AF.pdf %}
-
diff --git a/source/resume.md.bak b/source/resume.md.bak
deleted file mode 100644
index c9bf6ed5..00000000
--- a/source/resume.md.bak
+++ /dev/null
@@ -1,149 +0,0 @@
----
-title: 简历
-comments: false
-date: 2017-07-27 09:44:41
-permalink: resume
----
-# 联系方式
-* 手机:17326075631
-* Email:shenyangkai1994@gmail.com
-* QQ:237497819
-
-# 个人信息
-* 沈扬凯 / 男 / 1994
-* 湖北文理学院 / 物联网工程专业 / 本科 / 工学学士
-* 技术博客:https://xkcoding.com
-
-# 工作经历
-## 西安图迹信息科技有限公司 - 北京分公司(2016.08 ~ 2018.03)
-
-- 2017.04-2018.3,回到北京分公司,独立开发“晶见资讯”小程序,协助部署devops平台,参与设计并开发“图迹售电”云平台
-- 2016.08-2017.3,驻场在“龙源电力”集团,参与“风功率预测”平台v1.0和v2.0版的开发
-
-## 武汉金智维科技有限公司(2015.06~2016.07)
-
-参与接口的开发,平台的维护,参与“襄阳变电站设备检测管理系统”的开发
-
-# 项目经历
-
-## 西安图迹信息科技有限公司 - 北京分公司
-
-### 图迹售电云平台
-
-**项目描述:**
-图迹售电云平台分为售电侧、用电侧和管理侧。
-售电侧主要为售电公司提供客户管理、客户数据分析,合同管理、合同套餐推荐、合同数据分析,交易中心结算、客户结算、居间商结算以及结算分析和生成结算报表,负荷预测,交易模拟,交易复盘,资讯消息,系统管理等功能。(网址:http://sd.togeek.cn/em/gddn/)
-用电侧主要功能用电用户合同信息的展示,电量申报,资讯消息等功能。(网址:http://sd.togeek.cn/wt/gddn/)
-管理侧主要负责整个云平台账户维护,售电侧和用电侧模块权限控制等。(待开发)
-
-**主要负责:**
-1.根据不同省份的规则不同设计系统架构;
-2.负责开发合同服务、结算服务、资讯服务、权限服务、申报服务、日志服务。
-
-**主要工作:**
-1.负责开发结算服务、资讯服务、权限服务、日志服务,并根据全国各个省份电改政策做定制化开发。整个云平台使用Eureka集群搭建高可用的服务注册和发现组件,使用`Hystrix`对服务做熔断保护措施;
-2.结算服务是整个云平台请求最多的服务,这里采用`Ribbon`实现了该服务的负载均衡,该服务涉及到与其他服务的交互,通过`Feign`组件对其他服务进行调用,并使用`Sleuth`、`Zipkin`进行追踪服务的调用关系;
-3.资讯服务通过爬虫定时抓取与"售电"业务相关的信息,存入数据库,然后通过`kafka`消息队列,将资讯内容存入 `ElasticSearch` 中;
-4.权限服务采用`JWT`+`shiro`来实现;
-5.日志服务采用`logbook`来打印HTTP请求与响应,并通过`logback`保存至日志文件中,使用`ELK`对日志进行分析;
-6.整个云平台采用了前后端分离架构, Gateway使用`Zuul`进行路由控制,前端使用`React`技术栈和`Ant design`的UI层表现;
-7.数据库采用`shardbatis`进行分表操作。
-
-### 搭建DevOps平台
-
-**项目描述:**
-协助驻场在深圳"华润电力"集团的同事搭建DevOps平台。
-
-**主要工作:**
-1.搭建`GitLab`,`Jenkins`,`Docker`,`Harbor`环境;
-2.对整个流程进行测试。
-> 代码托管在`GitLab`中,对 `GitLab` 的push操作进行 `Jenkins` 的自动化代码风格的检测(`FireLine`);将代码风格合格之后的代码进行自动化打包成 `Docker` 镜像,并将打包成功的镜像push至 `Harbor` 仓库,并发送邮件给开发、测试、运维同事;开发环境下,`Jenkins` 根据编写好的 `YAML` 文件直接更新 `kubernetes` 中的应用;任何一个步骤出错,均触发邮件提醒。
-
-### "晶见资讯" 微信小程序
-
-**项目描述:**
-"晶见资讯"是一款关于售电信息检索的微信小程序。旨在帮助售电公司相关人员了解售电信息,减轻公司资讯人员的工作压力。"晶见资讯"分为admin端和小程序端,admin端主要是管理员对售电信息的管理和配置爬取的公众号信息,小程序端主要是面向售电咨询人员对售电信息的查询和下载。售电信息主要包括日常售电讯息、售电政策法规、专业问题在线咨询等。服务端网址: http://sd.togeek.cn/wechat/
-
-**主要工作:**
-1.使用轻量级开发框架`JFinal`来提供小程序入口及后台管理入口,使用`Vue.js`,`iView`搭建后台管理系统前端界面;
-2.管理端可以配置爬虫所需要抓取的公众号信息,使用`Java`编写的爬虫每天爬取公众号文章,存入`MySQL`,通过`kafka`消息队列,将爬取到的文章存入 `Elasticsearch`;
-3.管理端支持上传文档,文档支持PDF和Word,Word使用`POI`解析、PDF使用Apache的`pdfbox`解析入库,通过`kafka`消息队列,将新入库的文档信息存入 `Elasticsearch`中;
-4.小程序端信息存储、分类、检索使用`Elasticsearch`实现。
-
-### 风电场功率预测管理平台 v2.0
-
-**项目描述:**
-根据国网电力系统二次安防的要求,加入正向隔离,对原有v1.0平台进行重构。
-"风功率预测"平台分为现场端程序和服务端程序。现场端程序主要是用来定时采集风场实时数据,定时生成电力调度所需要的调度E文件并上传至调度。服务端程序主要用于运维人员每天的检查调度文件的上送情况,以及风场人员对风场数据的查看。服务端网址:http://wpps.clypg.com.cn
-
-**主要工作:**
-1.负责服务端程序的整体重构,UI层面使用了 `H-ui.admin`,并加入了`vue.js`和`iView`,数据库从`MySQL`改成`Oracle`数据库,并搭建了`Rac`实现高可用;
-2.现场端程序内外网程序采用`json`文件通过正反向隔离传输的方式进行数据同步,并使用`BeautyEye`美化`Swing`程序;
-3.现场端程序的在线升级,外网程序每天定时访问服务端是否有最新程序包,将最新的程序包通过`Base64`的encode成文本文件传输至内网,在decode回程序包进行升级。
-
-**成果:**
-龙源电力集团下总共有130多家风场,风功率预测现场端程序已**成功部署风场120多家风场**,现场端程序为风场E文件上送调度的**准确率提升至98%以上**,服务端程序为甲方运维人员和风场提供统一的查询以及申报界面,方便了运维工作,提升了工作效率,获得甲方和风场的一致好评。
-
-### 风功率预测管理平台 v1.0
-
-**项目描述:**
-风功率预测平台分为现场端和服务端程序,服务端程序部署在北京龙源集团总部,主要用于各个风场采集到的数据的展示以及定时从FTP获取各个风电场的短期预测文件,现场端程序部署在各个风场的内外网,分为内网程序和外网程序,外网程序主要是采集各个测风塔与风机的实时数据以及和服务端进行数据同步,并按照调度要求定时生成调度 E 文件,通过反向隔离设备将E文件传输给内网程序,内网程序收到调度文件,通过 FTP 协议将文件传输至调度侧,并将传输结果返回给外网程序,以便将结果同步至服务端。
-
-**主要工作:**
-1.服务端使用 `Quartz` 定时从 `FTP` 获取短期预测文件,并解析入库;
-2.使用 `HttpClient` 保证服务端与现场端外网程序的数据同步;
-3.现场端外网程序使用`Quartz`和`Freemarker`定时生成调度E文件,现场端内网程序使用`FTPClient`将调度E文件传输至调度侧的FTP服务器。
-
-## 武汉金智维科技有限公司
-
-### 襄阳变电站设备检测管理系统
-
-**项目描述:**
-主要对襄阳市各个变电站站点的运行监控,以及设备资源数据管理,对站点的运行提供故障分析和解决方案,对设备的运行提供实时校准、检修、验收的管理体制,保证了所有站点和运行设备正常运行,和对数据的分析、统计;设备的购置和费用报销由公司财务人员及有关部门领导共同介入审核,保证录入数据的准确性,合理性;并实现公司每个站点资料图书管理,方便的为公司技术人员提供最新的设备图书资料,给公司技术人员提供强大的技术支持;使用开源的echarts图表组件,生成设备运行情况,有利于技术人员做故障分析。
-
-**主要工作:**
-负责开发模块包括技术设施维护管理、站点管理、图书资料信息管理、系统管理4个功能模块,实现系统的功能设计与开发、页面设计与开发、数据库设计。除实现功能代码外,并编写系统的概要设计、详细设计文档及用户使用手册。
-
-**涉及技术:**
-POI 导入和导出;富文本编辑器 Ueditor 处理多格式文本内容;quartz定时备份数据库;echarts 来展示设备年月周的运行情况。
-
-### 华中科技大学教室管理系统
-
-**项目描述:**
-为了简化华中科技大学每年繁重的教室分配和分发权力,让各院老师或者学生自己去借用教室,而学校只需要从中调节一下不合理的教室申请就可以了,免去了以前查教室,然后考虑该教室是否可用等一系列繁复的操作,只保留现在的审核就可以了。也符合了现在信息化管理的条件,与时俱进,迎合了现代人的想法化繁为简,也简化了老师们的工作量。
-
-**主要工作:**
-主要负责教室申请整个流程的设计,流程分为老师(学生)申请、院系审核、教务处审核等一些功能的实现,并且设计相应的表结构和以及后台逻辑的开发。主要使用了jquery与ajax实现前端效果展示与前后台的数据交换工作,以及struts2自带的上传文件功能。
-
-### 谷城县再生资源回收利用平台
-
-**项目描述:**
-主要是提供一个在线的可再生资源信息发布平台,包括的内容有信息的发布和求购,网点的分布和资源你的推送。实现再生资源线上预约线下回收、分拣、交易、拍卖一体化的再生资源回收交易服务平台。 利用基于LBS的全方位O2O模式为产废、收废、利废环节搭建成一个最权威、最安全、最快捷、最方便的再生资源回收交易服务平台。该系统在功能上包括用户管理、公告管理、信息发布、审批管理、评论、位置服务、线上支付、首页管理。
-
-**主要工作:**
-主要负责信息发布、用户管理模块的开发,并且配合参与审批管理以及框架的搭建和数据库的设计。我的任务重心在信息发布上、主要使用JQuery与Ajax完成异步交互,后台使用内部MVC框架实现数据的交互。
-
-# 技能清单
-
-1. 有扎实的`Java`基础和面向对象抽象能力,有扎实的编程基础和良好的编程习惯;
-2. 熟练掌握`Spring`、`SpringMVC`、`Spring Boot`、`JFinal`、`mybatis`、`Hibernate`、`JPA`、`Dubbo`、`Zookeeper`等开源框架的使用;
-3. 熟悉`MySQL`、`Oracle`、`MSSQL`数据库,熟悉`Mysql`的主从复制,读写分离,熟悉sql优化;
-4. 熟练使用`JSP`、`FreeMarker`、`Beetl`、`Thymeleaf`模板引擎技术;
-5. 熟练使用`Quartz`、`Spring Task`定时任务;
-6. 熟练使用`maven`、`gradle`构建项目;
-7. 熟练使用`redis`缓存,并实现`redis`集群;
-8. 熟练使用`rabbitMQ`、`kafka`消息中间件;
-9. 熟练使用`best-pay-sdk`、`IJPay`开发微信支付、支付宝支付;
-10. 熟悉`ELK`技术栈,熟练使用`ElasticSearch`进行文件存储和检索,及集群配置,掌握`Kibana`、`Logstash`;
-11. 熟练使用`Apache Commons`、`Google Guava`、`hutool`等开源工具类;
-12. 掌握`Docker`,`Jenkins`,了解`DevOps`;
-13. 熟练使用`React.js`,熟悉`Redux`、`Mobx`数据状态管理,能独立开发常用组件;
-14. 熟练使用`Html5+Mui`完成跨平台App开发,适配安卓、IOS;
-15. 熟练使用`Vue.js`技术栈。
-
-# 我的开源项目
-`springboot` 学习项目 [spring-boot-demo](https://github.com/xkcoding/spring-boot-demo)
-
-# 致谢
-感谢您花时间阅读我的简历,期待能有机会和您共事。
-
diff --git a/source/robots.txt b/source/robots.txt
deleted file mode 100644
index 6f27bb66..00000000
--- a/source/robots.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-User-agent: *
-Disallow:
\ No newline at end of file
diff --git a/source/tags/index.md b/source/tags/index.md
deleted file mode 100644
index fa653871..00000000
--- a/source/tags/index.md
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: 分类 & 标签
-date: 2016-03-21 20:27:39
-type: "tags"
-comments: false
----
diff --git a/source/the-gift-for-hy.md b/source/the-gift-for-hy.md
deleted file mode 100644
index 41c6a6de..00000000
--- a/source/the-gift-for-hy.md
+++ /dev/null
@@ -1,29 +0,0 @@
----
-comments: false
-date: 2017-07-27 09:44:41
-permalink: the-gift-for-hy
----
-# 此表赠黄宇
-
-
-| 技能 | 掌握程度 |
-| :-: | :-: |
-| 基础 | ❤❤❤❤❤❤❤❤❤❤ (文件操作、多线程,尽量掌握) |
-| 数据库 | ❤❤❤❤❤❤❤❤ (增删改查,左右关联,自关联,子查询,分组,去重) |
-| HTML | ❤❤❤❤❤❤ (认得,可以写简单的页面,能在别人的基础上修改) |
-| CSS | ❤❤❤❤ (基础的会就行) |
-| JS | ❤❤❤❤❤ (基础 dom 操作必须会) |
-| JQuery | ❤❤❤❤❤❤ (重点掌握选择器的用法) |
-| sevlet | ❤❤❤❤❤❤ (请掌握生命周期) |
-| JSP | ❤❤❤❤❤❤ (会用就好,能看懂就好) |
-| Struts | ❤❤❤❤❤❤❤❤ (虽然落伍了。但是学好了便于理解) |
-| Hibernate | ❤❤❤❤❤❤❤❤ |
-| Spring | ❤❤❤❤❤❤❤❤❤❤ (一定一定要学好) |
-| SpringMVC | ❤❤❤❤❤❤❤❤❤❤ (外面大部分用这个,不管教不教,请学好,应该教的) |
-| Mybatis | ❤❤❤❤❤❤❤❤❤❤ (外面大部分用这个,不管教不教,请学好,应该教的) |
-| maven | ❤❤❤❤❤❤❤❤ (要掌握,会用就行,不需要了解原理) |
-| svn、git | ❤❤❤❤❤❤❤❤ (要掌握,会用就行,不需要了解原理) |
-| 新技术要好好在网上关注,学会看文档
技术不重要,学习新技术的技能很重要
| ❤❤❤❤❤❤❤❤❤❤ |
-
-
-
diff --git a/src/assets/icons/IconArchive.svg b/src/assets/icons/IconArchive.svg
new file mode 100644
index 00000000..e7e53af5
--- /dev/null
+++ b/src/assets/icons/IconArchive.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/IconArrowLeft.svg b/src/assets/icons/IconArrowLeft.svg
new file mode 100644
index 00000000..6bb4b116
--- /dev/null
+++ b/src/assets/icons/IconArrowLeft.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/IconArrowNarrowUp.svg b/src/assets/icons/IconArrowNarrowUp.svg
new file mode 100644
index 00000000..ac166a36
--- /dev/null
+++ b/src/assets/icons/IconArrowNarrowUp.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/IconArrowRight.svg b/src/assets/icons/IconArrowRight.svg
new file mode 100644
index 00000000..a0f23ae6
--- /dev/null
+++ b/src/assets/icons/IconArrowRight.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/IconBrandX.svg b/src/assets/icons/IconBrandX.svg
new file mode 100644
index 00000000..0feee2bf
--- /dev/null
+++ b/src/assets/icons/IconBrandX.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/IconCalendar.svg b/src/assets/icons/IconCalendar.svg
new file mode 100644
index 00000000..5f0be650
--- /dev/null
+++ b/src/assets/icons/IconCalendar.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/IconChevronLeft.svg b/src/assets/icons/IconChevronLeft.svg
new file mode 100644
index 00000000..2a1d7887
--- /dev/null
+++ b/src/assets/icons/IconChevronLeft.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/IconChevronRight.svg b/src/assets/icons/IconChevronRight.svg
new file mode 100644
index 00000000..41384b9d
--- /dev/null
+++ b/src/assets/icons/IconChevronRight.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/IconEdit.svg b/src/assets/icons/IconEdit.svg
new file mode 100644
index 00000000..de37f5ef
--- /dev/null
+++ b/src/assets/icons/IconEdit.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/IconFacebook.svg b/src/assets/icons/IconFacebook.svg
new file mode 100644
index 00000000..47ad743e
--- /dev/null
+++ b/src/assets/icons/IconFacebook.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/IconGitHub.svg b/src/assets/icons/IconGitHub.svg
new file mode 100644
index 00000000..ad12afd2
--- /dev/null
+++ b/src/assets/icons/IconGitHub.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/IconHash.svg b/src/assets/icons/IconHash.svg
new file mode 100644
index 00000000..7f80eaa2
--- /dev/null
+++ b/src/assets/icons/IconHash.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/IconLinkedin.svg b/src/assets/icons/IconLinkedin.svg
new file mode 100644
index 00000000..a98e1513
--- /dev/null
+++ b/src/assets/icons/IconLinkedin.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/IconLogo.svg b/src/assets/icons/IconLogo.svg
new file mode 100644
index 00000000..127bf54c
--- /dev/null
+++ b/src/assets/icons/IconLogo.svg
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/assets/icons/IconMail.svg b/src/assets/icons/IconMail.svg
new file mode 100644
index 00000000..cecc7199
--- /dev/null
+++ b/src/assets/icons/IconMail.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/IconMenuDeep.svg b/src/assets/icons/IconMenuDeep.svg
new file mode 100644
index 00000000..5d951a73
--- /dev/null
+++ b/src/assets/icons/IconMenuDeep.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/IconMoon.svg b/src/assets/icons/IconMoon.svg
new file mode 100644
index 00000000..ec6ba710
--- /dev/null
+++ b/src/assets/icons/IconMoon.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/IconPinterest.svg b/src/assets/icons/IconPinterest.svg
new file mode 100644
index 00000000..a398d347
--- /dev/null
+++ b/src/assets/icons/IconPinterest.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/IconRss.svg b/src/assets/icons/IconRss.svg
new file mode 100644
index 00000000..77515566
--- /dev/null
+++ b/src/assets/icons/IconRss.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/IconSearch.svg b/src/assets/icons/IconSearch.svg
new file mode 100644
index 00000000..0f6d8218
--- /dev/null
+++ b/src/assets/icons/IconSearch.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/IconSunHigh.svg b/src/assets/icons/IconSunHigh.svg
new file mode 100644
index 00000000..99026223
--- /dev/null
+++ b/src/assets/icons/IconSunHigh.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/IconTelegram.svg b/src/assets/icons/IconTelegram.svg
new file mode 100644
index 00000000..113242f0
--- /dev/null
+++ b/src/assets/icons/IconTelegram.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/IconWhatsapp.svg b/src/assets/icons/IconWhatsapp.svg
new file mode 100644
index 00000000..2e2de46f
--- /dev/null
+++ b/src/assets/icons/IconWhatsapp.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/IconX.svg b/src/assets/icons/IconX.svg
new file mode 100644
index 00000000..79f489b3
--- /dev/null
+++ b/src/assets/icons/IconX.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/images/AstroPaper-v3.png b/src/assets/images/AstroPaper-v3.png
new file mode 100644
index 00000000..7b967413
Binary files /dev/null and b/src/assets/images/AstroPaper-v3.png differ
diff --git a/src/assets/images/AstroPaper-v4.png b/src/assets/images/AstroPaper-v4.png
new file mode 100644
index 00000000..b4de5577
Binary files /dev/null and b/src/assets/images/AstroPaper-v4.png differ
diff --git a/src/assets/images/AstroPaper-v5.png b/src/assets/images/AstroPaper-v5.png
new file mode 100644
index 00000000..8fb49856
Binary files /dev/null and b/src/assets/images/AstroPaper-v5.png differ
diff --git a/src/assets/images/forrest-gump-quote.png b/src/assets/images/forrest-gump-quote.png
new file mode 100644
index 00000000..37bdb35b
Binary files /dev/null and b/src/assets/images/forrest-gump-quote.png differ
diff --git a/src/components/BackButton.astro b/src/components/BackButton.astro
new file mode 100644
index 00000000..4cbc16eb
--- /dev/null
+++ b/src/components/BackButton.astro
@@ -0,0 +1,37 @@
+---
+import IconChevronLeft from "@/assets/icons/IconChevronLeft.svg";
+import LinkButton from "./LinkButton.astro";
+import { SITE } from "@/config";
+---
+
+{
+ SITE.showBackButton && (
+
+
+
+ 返回
+
+
+ )
+}
+
+
diff --git a/src/components/BackToTopButton.astro b/src/components/BackToTopButton.astro
new file mode 100644
index 00000000..0b1e654a
--- /dev/null
+++ b/src/components/BackToTopButton.astro
@@ -0,0 +1,88 @@
+---
+import IconChevronLeft from "@/assets/icons/IconChevronLeft.svg";
+import IconArrowNarrowUp from "@/assets/icons/IconArrowNarrowUp.svg";
+---
+
+
+
+
+
+
+
+ 返回顶部
+
+
+
+
+
diff --git a/src/components/Breadcrumb.astro b/src/components/Breadcrumb.astro
new file mode 100644
index 00000000..0f806895
--- /dev/null
+++ b/src/components/Breadcrumb.astro
@@ -0,0 +1,71 @@
+---
+// Remove current url path and remove trailing slash if exists
+const currentUrlPath = Astro.url.pathname.replace(/\/+$/, "");
+
+// Get url array from path
+// eg: /tags/tailwindcss => ['tags', 'tailwindcss']
+const breadcrumbList = currentUrlPath.split("/").slice(1);
+
+// 路径名称映射
+const pathNameMap: Record = {
+ posts: "文章",
+ tags: "标签",
+ about: "关于",
+ archives: "归档",
+ search: "搜索",
+};
+
+// if breadcrumb is 首页 > 文章 > 1
+// replace 文章 with 文章 (第 X 页)
+if (breadcrumbList[0] === "posts") {
+ const page = Number(breadcrumbList[1]) || 1;
+ breadcrumbList.splice(0, 2, `文章${page > 1 ? ` (第 ${page} 页)` : ""}`);
+}
+
+// if breadcrumb is 首页 > 标签 > [tag] > [page]
+// replace [tag] > [page] with [tag] (第 X 页)
+if (breadcrumbList[0] === "tags" && !isNaN(Number(breadcrumbList[2]))) {
+ breadcrumbList.splice(
+ 1,
+ 3,
+ `${breadcrumbList[1]}${Number(breadcrumbList[2]) === 1 ? "" : " (第 " + breadcrumbList[2] + " 页)"}`
+ );
+}
+
+// 获取显示名称
+function getDisplayName(path: string): string {
+ return pathNameMap[path] || path;
+}
+---
+
+
+
+
+ 首页
+ »
+
+ {
+ breadcrumbList.map((breadcrumb, index) =>
+ index + 1 === breadcrumbList.length ? (
+
+ 0 }]}
+ aria-current="page"
+ >
+ {decodeURIComponent(getDisplayName(breadcrumb))}
+
+
+ ) : (
+
+
+ {getDisplayName(breadcrumb)}
+
+ »
+
+ )
+ )
+ }
+
+
diff --git a/src/components/Card.astro b/src/components/Card.astro
new file mode 100644
index 00000000..7924174f
--- /dev/null
+++ b/src/components/Card.astro
@@ -0,0 +1,80 @@
+---
+import type { CollectionEntry } from "astro:content";
+import { slugifyStr } from "@/utils/slugify";
+import { getPath } from "@/utils/getPath";
+import Datetime from "./Datetime.astro";
+
+type Props = {
+ variant?: "h2" | "h3";
+ class?: string;
+ "data-weight"?: string | number;
+ style?: string;
+ section?: string;
+} & CollectionEntry<"blog">;
+
+const {
+ variant: Heading = "h2",
+ id,
+ data,
+ filePath,
+ body,
+ class: className,
+ "data-weight": dataWeight,
+ style,
+ section = "",
+} = Astro.props;
+
+const { title, description, featured, ...props } = data;
+
+// 自动生成摘要:如果 description 为空,从文章内容提取
+function getExcerpt(content: string, maxLength: number = 350): string {
+ // 移除 frontmatter、HTML 标签、Markdown 语法
+ const cleaned = content
+ .replace(/---[\s\S]*?---/, "") // 移除 frontmatter
+ .replace(//g, "") // 移除 more 标记
+ .replace(/<[^>]+>/g, "") // 移除 HTML 标签
+ .replace(/!\[.*?\]\(.*?\)/g, "") // 移除图片
+ .replace(/\[([^\]]+)\]\([^)]+\)/g, "$1") // 保留链接文字
+ .replace(/^#+\s+/gm, "") // 移除标题标记
+ .replace(/[*_`~]/g, "") // 移除强调标记
+ .replace(/>\s+/g, "") // 移除引用标记
+ .replace(/\n+/g, " ") // 换行转空格
+ .trim();
+
+ if (cleaned.length <= maxLength) return cleaned;
+ return cleaned.slice(0, maxLength).trim() + "...";
+}
+
+const excerpt = description || (body ? getExcerpt(body) : "");
+---
+
+
+
+
+ {excerpt && {excerpt}
}
+
diff --git a/src/components/Datetime.astro b/src/components/Datetime.astro
new file mode 100644
index 00000000..5a1a3c0c
--- /dev/null
+++ b/src/components/Datetime.astro
@@ -0,0 +1,57 @@
+---
+import dayjs from "dayjs";
+import utc from "dayjs/plugin/utc";
+import timezone from "dayjs/plugin/timezone";
+import "dayjs/locale/zh-cn";
+import IconCalendar from "@/assets/icons/IconCalendar.svg";
+import { SITE } from "@/config";
+
+dayjs.extend(utc);
+dayjs.extend(timezone);
+dayjs.locale("zh-cn");
+
+type Props = {
+ class?: string;
+ size?: "sm" | "lg";
+ pubDatetime: string | Date;
+ timezone?: string;
+ modDatetime?: string | Date | null;
+};
+
+const {
+ pubDatetime,
+ modDatetime,
+ size = "sm",
+ class: className = "",
+ timezone: postTimezone,
+} = Astro.props;
+
+/* ========== Formatted Datetime ========== */
+const isModified = modDatetime && modDatetime > pubDatetime;
+
+const datetime = dayjs(isModified ? modDatetime : pubDatetime).tz(
+ postTimezone || SITE.timezone
+);
+
+const date = datetime.format("YYYY年M月D日"); // e.g., '2025年3月22日'
+---
+
+
+
+ {
+ isModified && (
+
+ 更新于
+
+ )
+ }
+ {date}
+
diff --git a/src/components/EditPost.astro b/src/components/EditPost.astro
new file mode 100644
index 00000000..fe3726ba
--- /dev/null
+++ b/src/components/EditPost.astro
@@ -0,0 +1,34 @@
+---
+import type { CollectionEntry } from "astro:content";
+import IconEdit from "@/assets/icons/IconEdit.svg";
+import { SITE } from "@/config";
+
+type Props = {
+ hideEditPost?: CollectionEntry<"blog">["data"]["hideEditPost"];
+ class?: string;
+ post: CollectionEntry<"blog">;
+};
+
+const { hideEditPost, post, class: className = "" } = Astro.props;
+
+const href = `${SITE.editPost.url}${post.filePath}`;
+const showEditPost =
+ SITE.editPost.enabled && !hideEditPost && href.trim() !== "";
+---
+
+{
+ showEditPost && (
+
+
+ {SITE.editPost.text}
+
+ )
+}
diff --git a/src/components/Footer.astro b/src/components/Footer.astro
new file mode 100644
index 00000000..41c8ef00
--- /dev/null
+++ b/src/components/Footer.astro
@@ -0,0 +1,45 @@
+---
+import type { HTMLAttributes } from "astro/types";
+
+const currentYear = new Date().getFullYear();
+
+type Props = {
+ noMarginTop?: boolean;
+} & HTMLAttributes<"footer">;
+
+const { noMarginTop = false, class: className, ...attrs } = Astro.props;
+---
+
+
diff --git a/src/components/Header.astro b/src/components/Header.astro
new file mode 100644
index 00000000..79b7ab35
--- /dev/null
+++ b/src/components/Header.astro
@@ -0,0 +1,179 @@
+---
+import IconX from "@/assets/icons/IconX.svg";
+import IconMoon from "@/assets/icons/IconMoon.svg";
+import IconSearch from "@/assets/icons/IconSearch.svg";
+import IconArchive from "@/assets/icons/IconArchive.svg";
+import IconSunHigh from "@/assets/icons/IconSunHigh.svg";
+import IconMenuDeep from "@/assets/icons/IconMenuDeep.svg";
+import IconLogo from "@/assets/icons/IconLogo.svg";
+import LinkButton from "./LinkButton.astro";
+import { SITE } from "@/config";
+
+const { pathname } = Astro.url;
+
+// Remove trailing slash from current pathname if exists
+const currentPath =
+ pathname.endsWith("/") && pathname !== "/" ? pathname.slice(0, -1) : pathname;
+
+const isActive = (path: string) => {
+ const currentPathArray = currentPath.split("/").filter(p => p.trim());
+ const pathArray = path.split("/").filter(p => p.trim());
+
+ return currentPath === path || currentPathArray[0] === pathArray[0];
+};
+---
+
+
+ Skip to content
+
+
+
+
+
diff --git a/src/components/LinkButton.astro b/src/components/LinkButton.astro
new file mode 100644
index 00000000..cfcc1131
--- /dev/null
+++ b/src/components/LinkButton.astro
@@ -0,0 +1,21 @@
+---
+import type { HTMLAttributes } from "astro/types";
+
+type Props = { disabled?: boolean } & HTMLAttributes<"a">;
+
+const { disabled, class: className, ...attrs } = Astro.props;
+
+const Button = disabled ? "span" : "a";
+---
+
+
+
+
diff --git a/src/components/Pagination.astro b/src/components/Pagination.astro
new file mode 100644
index 00000000..1f6f3671
--- /dev/null
+++ b/src/components/Pagination.astro
@@ -0,0 +1,43 @@
+---
+import type { Page } from "astro";
+import type { CollectionEntry } from "astro:content";
+import IconArrowLeft from "@/assets/icons/IconArrowLeft.svg";
+import IconArrowRight from "@/assets/icons/IconArrowRight.svg";
+import LinkButton from "./LinkButton.astro";
+
+type Props = {
+ page: Page>;
+};
+
+const { page } = Astro.props;
+---
+
+{
+ page.lastPage > 1 && (
+
+
+
+ Prev
+
+ {page.currentPage} / {page.lastPage}
+
+ Next
+
+
+
+ )
+}
diff --git a/src/components/ShareLinks.astro b/src/components/ShareLinks.astro
new file mode 100644
index 00000000..948f6338
--- /dev/null
+++ b/src/components/ShareLinks.astro
@@ -0,0 +1,26 @@
+---
+import { SHARE_LINKS } from "@/constants";
+import LinkButton from "./LinkButton.astro";
+
+const URL = Astro.url;
+---
+
+{
+ SHARE_LINKS.length > 0 && (
+
+
分享到:
+
+ {SHARE_LINKS.map(social => (
+
+
+ {social.linkTitle}
+
+ ))}
+
+
+ )
+}
diff --git a/src/components/Socials.astro b/src/components/Socials.astro
new file mode 100644
index 00000000..e05f6986
--- /dev/null
+++ b/src/components/Socials.astro
@@ -0,0 +1,19 @@
+---
+import { SOCIALS } from "@/constants";
+import LinkButton from "./LinkButton.astro";
+---
+
+
+ {
+ SOCIALS.map(social => (
+
+
+ {social.linkTitle}
+
+ ))
+ }
+
diff --git a/src/components/Tag.astro b/src/components/Tag.astro
new file mode 100644
index 00000000..fbc6cf54
--- /dev/null
+++ b/src/components/Tag.astro
@@ -0,0 +1,35 @@
+---
+import IconHash from "@/assets/icons/IconHash.svg";
+
+type Props = {
+ tag: string;
+ tagName: string;
+ size?: "sm" | "lg";
+};
+
+const { tag, tagName, size = "lg" } = Astro.props;
+---
+
+
+
+
+ {tagName}
+
+
diff --git a/src/config.ts b/src/config.ts
new file mode 100644
index 00000000..f138e9a8
--- /dev/null
+++ b/src/config.ts
@@ -0,0 +1,23 @@
+export const SITE = {
+ website: "https://xkcoding.com/",
+ author: "xkcoding",
+ profile: "https://github.com/xkcoding",
+ desc: "xkcoding的代码成长日记",
+ title: "CodingDiary",
+ ogImage: "astropaper-og.jpg",
+ lightAndDarkMode: true,
+ postPerIndex: 8,
+ postPerPage: 10,
+ scheduledPostMargin: 15 * 60 * 1000, // 15 minutes
+ showArchives: true,
+ showBackButton: true,
+ editPost: {
+ enabled: true,
+ text: "编辑页面",
+ url: "https://github.com/xkcoding/xkcoding.github.io/edit/main/src/data/blog",
+ },
+ dynamicOgImage: true,
+ dir: "ltr",
+ lang: "zh-CN",
+ timezone: "Asia/Shanghai",
+} as const;
diff --git a/src/constants.ts b/src/constants.ts
new file mode 100644
index 00000000..aa9eafeb
--- /dev/null
+++ b/src/constants.ts
@@ -0,0 +1,70 @@
+import type { Props } from "astro";
+import IconMail from "@/assets/icons/IconMail.svg";
+import IconGitHub from "@/assets/icons/IconGitHub.svg";
+import IconBrandX from "@/assets/icons/IconBrandX.svg";
+import IconWhatsapp from "@/assets/icons/IconWhatsapp.svg";
+import IconFacebook from "@/assets/icons/IconFacebook.svg";
+import IconTelegram from "@/assets/icons/IconTelegram.svg";
+import IconPinterest from "@/assets/icons/IconPinterest.svg";
+import { SITE } from "@/config";
+
+interface Social {
+ name: string;
+ href: string;
+ linkTitle: string;
+ icon: (_props: Props) => Element;
+}
+
+export const SOCIALS: Social[] = [
+ {
+ name: "GitHub",
+ href: "https://github.com/xkcoding",
+ linkTitle: `${SITE.title} on GitHub`,
+ icon: IconGitHub,
+ },
+ {
+ name: "Mail",
+ href: "mailto:237497819@qq.com",
+ linkTitle: `Send an email to ${SITE.title}`,
+ icon: IconMail,
+ },
+] as const;
+
+export const SHARE_LINKS: Social[] = [
+ {
+ name: "WhatsApp",
+ href: "https://wa.me/?text=",
+ linkTitle: `Share this post via WhatsApp`,
+ icon: IconWhatsapp,
+ },
+ {
+ name: "Facebook",
+ href: "https://www.facebook.com/sharer.php?u=",
+ linkTitle: `Share this post on Facebook`,
+ icon: IconFacebook,
+ },
+ {
+ name: "X",
+ href: "https://x.com/intent/post?url=",
+ linkTitle: `Share this post on X`,
+ icon: IconBrandX,
+ },
+ {
+ name: "Telegram",
+ href: "https://t.me/share/url?url=",
+ linkTitle: `Share this post via Telegram`,
+ icon: IconTelegram,
+ },
+ {
+ name: "Pinterest",
+ href: "https://pinterest.com/pin/create/button/?url=",
+ linkTitle: `Share this post on Pinterest`,
+ icon: IconPinterest,
+ },
+ {
+ name: "Mail",
+ href: "mailto:?subject=See%20this%20post&body=",
+ linkTitle: `Share this post via email`,
+ icon: IconMail,
+ },
+] as const;
diff --git a/src/content.config.ts b/src/content.config.ts
new file mode 100644
index 00000000..6119aa62
--- /dev/null
+++ b/src/content.config.ts
@@ -0,0 +1,26 @@
+import { defineCollection, z } from "astro:content";
+import { glob } from "astro/loaders";
+import { SITE } from "@/config";
+
+export const BLOG_PATH = "src/data/blog";
+
+const blog = defineCollection({
+ loader: glob({ pattern: "**/[^_]*.md", base: `./${BLOG_PATH}` }),
+ schema: ({ image }) =>
+ z.object({
+ author: z.string().default(SITE.author),
+ pubDatetime: z.date(),
+ modDatetime: z.date().optional().nullable(),
+ title: z.string(),
+ featured: z.boolean().optional(),
+ draft: z.boolean().optional(),
+ tags: z.array(z.string()).default(["others"]),
+ ogImage: image().or(z.string()).optional(),
+ description: z.string(),
+ canonicalURL: z.string().optional(),
+ hideEditPost: z.boolean().optional(),
+ timezone: z.string().optional(),
+ }),
+});
+
+export const collections = { blog };
diff --git a/src/data/blog/2017-07-12.jdk-map-source-explore.md b/src/data/blog/2017-07-12.jdk-map-source-explore.md
new file mode 100644
index 00000000..433fd31c
--- /dev/null
+++ b/src/data/blog/2017-07-12.jdk-map-source-explore.md
@@ -0,0 +1,150 @@
+---
+title: "HashMap 的 JDK 源码探索"
+author: xkcoding
+pubDatetime: 2017-07-12T03:26:43.000Z
+slug: jdk-map-source-explore
+featured: false
+draft: false
+tags:
+ - java
+ - 源码分析
+description: "Map.Entry 明明是个接口,为什么能直接调用 getKey() 和 getValue()?从一个朋友的疑问出发,深入 HashMap 源码,揭秘 entrySet() 返回的其实是内部类 EntrySet,而真正干活的是实现了 Map.Entry 的 Node 类。一次完整的 Java 多态实战分析,帮你理解接口、实现类、内部类之间的关系。"
+---
+
+> 缘由:今天好友拿着下面的代码,问我为什么Map.Entry 这个接口没有实现 getKey() 和 getValue() 方法,却可以使用,由此,开启了一番查阅 JDK 源码的旅途....
+
+
+
+```java
+Map map = new HashMap();
+
+map.put(1, "张三");
+map.put(2, "李四");
+map.put(3, "王五");
+map.put(4, "赵六");
+map.put(5, "钱七");
+
+Set set = map.entrySet();
+for (Object object : set) {
+ Map.Entry entry = (Map.Entry) object;
+ System.out.println(entry.getKey() + "-->" + entry.getValue());
+}
+```
+
+1. 首先,我们看 `map` 对象,这个 `map` 对象是 `HashMap` 的一个实例,然后下面的 `Set set = map.entrySet();` 可以知道这其实用的 `HashMap` 实现的 `entrySet()` 方法,然后我们可以查看 `HashMap` 里 `entrySet()` 的源码
+
+ 
+
+ 从源码可以看出,这里的返回了一个 `EntrySet` 对象,但是需要注意的是这个 `EntrySet` 是 `HashMap` 里的一个内部类,源码如下:
+
+ ```java
+ final class EntrySet extends AbstractSet> {
+ public final int size() {
+ return size;
+ }
+
+ public final void clear() {
+ HashMap.this.clear();
+ }
+
+ public final Iterator> iterator() {
+ return new EntryIterator();
+ }
+
+ public final boolean contains(Object o) {
+ if (!(o instanceof Map.Entry))
+ return false;
+ Map.Entry,?> e = (Map.Entry,?>) o;
+ Object key = e.getKey();
+ Node candidate = getNode(hash(key), key);
+ return candidate != null && candidate.equals(e);
+ }
+
+ public final boolean remove(Object o) {
+ if (o instanceof Map.Entry) {
+ Map.Entry,?> e = (Map.Entry,?>) o;
+ Object key = e.getKey();
+ Object value = e.getValue();
+ return removeNode(hash(key), key, value, true, true) != null;
+ }
+ return false;
+ }
+
+ public final Spliterator> spliterator() {
+ return new EntrySpliterator<>(HashMap.this, 0, -1, 0, 0);
+ }
+
+ public final void forEach(Consumer super Map.Entry> action) {
+ Node[] tab;
+ if (action == null)
+ throw new NullPointerException();
+ if (size > 0 && (tab = table) != null) {
+ int mc = modCount;
+ for (int i = 0; i < tab.length; ++i) {
+ for (Node e = tab[i]; e != null; e = e.next)
+ action.accept(e);
+ }
+ if (modCount != mc)
+ throw new ConcurrentModificationException();
+ }
+ }
+ }
+ ```
+
+ 从这里我们是可以看出,这个 `EntrySet` 其实是封装的一个 `Node` 类的实体。也就是说我们的 `set` 其实就是这个 `Node` 对象。
+
+2. 现在我们来说说这个 `Node` 对象,`Node` 对象也是 `HashMap` 里的一个内部类,源码如下:
+
+ ```java
+ static class Node implements Map.Entry {
+ final int hash;
+ final K key;
+ V value;
+ Node next;
+
+ Node(int hash, K key, V value, Node next) {
+ this.hash = hash;
+ this.key = key;
+ this.value = value;
+ this.next = next;
+ }
+
+ public final K getKey() {
+ return key;
+ }
+
+ public final V getValue() {
+ return value;
+ }
+
+ public final String toString() {
+ return key + "=" + value;
+ }
+
+ public final int hashCode() {
+ return Objects.hashCode(key) ^ Objects.hashCode(value);
+ }
+
+ public final V setValue(V newValue) {
+ V oldValue = value;
+ value = newValue;
+ return oldValue;
+ }
+
+ public final boolean equals(Object o) {
+ if (o == this)
+ return true;
+ if (o instanceof Map.Entry) {
+ Map.Entry,?> e = (Map.Entry,?>)o;
+ if (Objects.equals(key, e.getKey()) &&
+ Objects.equals(value, e.getValue()))
+ return true;
+ }
+ return false;
+ }
+ }
+ ```
+
+ 可以看出来,这个 `Node` 对象是 `Map.Entry` 的实现类,我们可以看到这个 `Node` 对象实现了 `getKey()` 和 `getValue()` 的方法,所以后面调用的 `entry.getKey()` 以及 `entry.getValue()` 方法其实都是调用的 Node 对象里的`getKey()` 和 `getValue()` 方法,这里就是 `Java` 的多态的一种表现。
+
+3. 至此,打完收枪!
diff --git a/src/data/blog/2017-07-19.spring-boot-mybatis-logback.md b/src/data/blog/2017-07-19.spring-boot-mybatis-logback.md
new file mode 100644
index 00000000..2252aa53
--- /dev/null
+++ b/src/data/blog/2017-07-19.spring-boot-mybatis-logback.md
@@ -0,0 +1,139 @@
+---
+title: "spring-boot整合mybatis的时候使用logback打印日志"
+author: xkcoding
+pubDatetime: 2017-07-19T03:44:55.000Z
+slug: spring-boot-mybatis-logback
+featured: false
+draft: false
+tags:
+ - spring-boot
+ - mybatis
+description: "Spring Boot 整合 MyBatis 后,SQL 语句死活打印不出来?关键在于 logback-spring.xml 里要把 Mapper 接口的包路径配成 DEBUG 级别。本文提供完整的 logback 配置模板,包括控制台输出、按日期滚动的文件日志、独立的错误日志。另外安利 Lombok 的 @Slf4j 注解,告别冗余的 Logger 声明。"
+---
+
+> 最近在学习 `spring boot` ,日志用的是 `logback` ,至于为什么没有选常用的 `log4j` ,因为 `logback` 是 `spring boot` 官方推荐的日志系统。但是在整合 `Mybatis` 的时候一直打印不了 `SQL` 语句,查阅多方资料,终于解决了~
+>
+>
+
+1. 先展示下 `logback-spring.xml` 的完整内容
+
+ ```xml
+
+
+
+
+ 【xkcoding】%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] %logger{36} - %msg%n
+
+
+
+
+
+
+
+ ${user.dir}/logs/log/online-study.%d.log
+
+
+ 【xkcoding】%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] %logger{36} - %msg%n
+
+
+
+
+
+ ERROR
+ ACCEPT
+ DENY
+
+
+
+
+ ${user.dir}/logs/error/online-study.%d.error
+
+
+ 【xkcoding】%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] %logger{36} - %msg%n
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ```
+
+2. 如果是 Log4J 实现的话可以参考[Mybatis 日志](http://www.mybatis.org/mybatis-3/zh/logging.html)
+
+3. 第一个节点 `consoleLog` 是在控制台显示的 Log 日志,在控制台显示 Log 日志需要在 `appender` 节点里配置 `class="ch.qos.logback.core.ConsoleAppender"` ,格式化输出的配置在 `pattern` 节点中配置,各个参数的介绍如下:
+
+ ```
+ %-5level: 显示 Log 级别,从左显示5个字符宽度,打印内容: TRACE/DEBUG/INFO/WARN/ERROR
+ %d{yyyy-MM-dd HH:mm:ss.SSS}: 显示时间戳,{}中内容代表时间戳格式
+ %thread: 显示线程名字
+ %logger{36}: 表示 logger 名字最长36个字符,否则按照句点分割
+ %msg: 日志消息
+ %n: 换行符
+ ```
+
+4. 第二个节点 `fileLog` 是将日志保存为文件,需要在 `appender` 节点里配置 ` class="ch.qos.logback.core.rolling.RollingFileAppender"` ,并且指定滚动策略,为每天一个文件,指定文件名为 `%d.log` 。
+
+ ```xml
+
+
+
+ ${user.dir}/logs/log/online-study.%d.log
+
+ ```
+
+5. 第三个节点 `fileErrorLog` 是将错误日志记录在文件中,不保存其他级别的日志,需要配置一个 `filter` 节点。拦截内容如下(`注意单词大小写,千万不能出错!`):
+
+ ```xml
+
+ ERROR
+ ACCEPT
+ DENY
+
+ ```
+
+6. 在这里再推荐一个 maven 依赖,`lombok`
+
+ ```xml
+
+ org.projectlombok
+ lombok
+
+ ```
+
+7. 在 idea 里安装 `lombok` 的相应插件
+ 
+
+8. 然后就可以在类名上方添加 `@Slf4j` 注解,这样就可以直接在代码中使用 `log.info("....")` 打印日志了。
+
+9. 再提示下 `lombok` 的其他使用技巧,如果使用了 `lombok` ,只要在实体类的类名上加上 `@Data` 注解,那么我们就不需要写 `getter/ setter/toString` 方法,`lombok` 会自动生成 `getter/setter/toString` 方法,我们直接调用就可以了,如果只需要生成 `getter` 或者 `setter`,就可以直接使用 `@Getter` 或者 `@Setter` 注解。并且 `@Getter` 和 `@Setter` 这两个注解不止可以用在 `Class` 上,还可以用在 `Enum` 上哦。
+
+10. 就介绍到这儿,enjoy it !!!
diff --git a/source/_posts/2017-08-02.customize-blocked-queue.md b/src/data/blog/2017-08-02.customize-blocked-queue.md
similarity index 66%
rename from source/_posts/2017-08-02.customize-blocked-queue.md
rename to src/data/blog/2017-08-02.customize-blocked-queue.md
index c459e180..d35c1c09 100644
--- a/source/_posts/2017-08-02.customize-blocked-queue.md
+++ b/src/data/blog/2017-08-02.customize-blocked-queue.md
@@ -1,27 +1,24 @@
---
-title: 自定义阻塞队列
-comments: true
-fancybox: false
-toc: false
-date: 2017-08-02 15:47:07
+title: "自定义阻塞队列"
+author: xkcoding
+pubDatetime: 2017-08-02T07:47:07.000Z
+slug: customize-blocked-queue
+featured: false
+draft: false
tags:
-- java
-- 多线程
-- 代码片段
-categories:
-- 代码片段
-- java
-description:
-permalink: customize-blocked-queue
-top:
+ - java
+ - 代码片段
+description: "想理解 Java 并发编程里的 wait() 和 notify()?最好的方式是自己实现一个阻塞队列。本文用 LinkedList + AtomicInteger + synchronized 手撸一个简易版 BlockingQueue,队列满时 put 阻塞,队列空时 get 阻塞,完美演示生产者-消费者模型。附完整测试代码,帮你真正理解线程间通信机制。"
---
今天重温了下 java 多线程中的 `notify()` 方法以及 `wait()` 方法,一时兴起,决定通过这俩个方法,实现一个简易的自定义阻塞队列。
+
+
> 阻塞队列是什么,与普通队列的区别是什么?
> 阻塞队列与普通队列的区别在于,当队列是空的时,从队列中获取元素的操作将会被阻塞,或者当队列是满时,往队列里添加元素的操作会被阻塞。试图从空的阻塞队列中获取元素的线程将会被阻塞,直到其他的线程往空的队列插入新的元素。同样,试图往已满的阻塞队列中添加新元素的线程同样也会被阻塞,直到其他的线程使队列重新变得空闲起来。
-1. 新建一个 `MyQueue.java` 类
+1. 新建一个 `MyQueue.java` 类
```java
import java.util.LinkedList;
@@ -113,23 +110,24 @@ top:
}
```
-2. 新建一个测试类 `MyQueueTest.java`,测试类中,我们初始化一个队列,并将元素填满,然后启动一个线程 `t1`,去插入数据,中间休眠2s,再去启动一个线程 `t2` 取数据。
+
+2. 新建一个测试类 `MyQueueTest.java`,测试类中,我们初始化一个队列,并将元素填满,然后启动一个线程 `t1`,去插入数据,中间休眠2s,再去启动一个线程 `t2` 取数据。
```java
import com.xiaoleilu.hutool.util.StrUtil;
-
+
import java.util.concurrent.TimeUnit;
-
+
public class MyQueueTest {
public static void main(String[] args) {
final MyQueue queue = new MyQueue(5);
-
+
queue.put("a");
queue.put("b");
queue.put("c");
queue.put("d");
queue.put("e");
-
+
System.out.println(StrUtil.format("当前队列的长度: {}", queue.getSize()));
Thread t1 = new Thread(() -> {
queue.put("f");
@@ -138,27 +136,31 @@ top:
}, "t1");
```
+ Thread t2 = new Thread(() -> {
+ queue.get();
+ queue.get();
+ });
+
+ t1.start();
+ try {
+ TimeUnit.SECONDS.sleep(2);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ t2.start();
+ }
- Thread t2 = new Thread(() -> {
- queue.get();
- queue.get();
- });
-
- t1.start();
- try {
- TimeUnit.SECONDS.sleep(2);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- t2.start();
- }
}
+
```
-3. 启动测试类,查看运行结果。控制台如果应该出现的效果是,队列先初始化完成,然后休眠2s,接下来先取数据,再插入数据,则证明阻塞队列生效。下面是控制台运行的效果:
- * 初始化队列
+
+ ```
+
+3. 启动测试类,查看运行结果。控制台如果应该出现的效果是,队列先初始化完成,然后休眠2s,接下来先取数据,再插入数据,则证明阻塞队列生效。下面是控制台运行的效果:
+ - 初始化队列

- * 休眠2s后取队首元素,再插入队尾元素
- 
- * 此时我们会发现,程序还未停止,因为此时队列已满,但是线程 `t1` 还未插入 `h` 元素,因此线程被阻塞着,直至下次队列有空余空间才会被唤醒。
-4. 至此,一个自定义阻塞队列就已经实现了。
-5. 细心的朋友会发现,我打印的 log 里用到了一个 `StrUtil.format()` 方法,这个和 `slf4j` 的 log 用法一致,可以使用占位符。这个是用到了一个国产良心工具类,[hutool](http://www.hutool.cn/),国产开源,需要大家的支持,觉得好用的话,期望可以去 [码云](https://github.com/looly/hutool) 或者 [github](http://git.oschina.net/loolly/hutool) 上给个 Star 吧!
\ No newline at end of file
+ - 休眠2s后取队首元素,再插入队尾元素
+ 
+ - 此时我们会发现,程序还未停止,因为此时队列已满,但是线程 `t1` 还未插入 `h` 元素,因此线程被阻塞着,直至下次队列有空余空间才会被唤醒。
+4. 至此,一个自定义阻塞队列就已经实现了。
+5. 细心的朋友会发现,我打印的 log 里用到了一个 `StrUtil.format()` 方法,这个和 `slf4j` 的 log 用法一致,可以使用占位符。这个是用到了一个国产良心工具类,[hutool](http://www.hutool.cn/),国产开源,需要大家的支持,觉得好用的话,期望可以去 [码云](https://github.com/looly/hutool) 或者 [github](http://git.oschina.net/loolly/hutool) 上给个 Star 吧!
diff --git a/source/_posts/2017-08-31.mysql-start-error.md b/src/data/blog/2017-08-31.mysql-start-error.md
similarity index 59%
rename from source/_posts/2017-08-31.mysql-start-error.md
rename to src/data/blog/2017-08-31.mysql-start-error.md
index eff9ffec..744e6e66 100644
--- a/source/_posts/2017-08-31.mysql-start-error.md
+++ b/src/data/blog/2017-08-31.mysql-start-error.md
@@ -1,19 +1,17 @@
---
-title: mysql启动报错问题
-comments: true
-fancybox: true
-toc: false
-date: 2017-08-31 09:48:10
+title: "mysql启动报错问题"
+author: xkcoding
+pubDatetime: 2017-08-31T01:48:10.000Z
+slug: mysql-start-error
+featured: false
+draft: false
tags:
-- mysql
-categories:
-- 技术相关
-- mysql
-description:
-permalink: mysql-start-error
-top:
+ - mysql
+description: "Windows 服务器上 MySQL 启动失败,报「系统错误 1067 进程意外终止」?别急着改 my.ini 的 basedir 和 datadir,先用 netstat 查查 3306 端口是不是被占用了。本文记录一次排查过程:发现是 IEXPLORE.exe 占了端口,杀掉进程后 MySQL 正常启动。附 Windows 下端口占用排查命令。"
---
+
今天公司 windows 服务器 `MySQL` 数据库启动异常,记录下原因。
+
## 1. 问题重现
@@ -44,4 +42,4 @@ tasklist|findstr "2016"
## 3. 解决问题
-打开任务管理器, IEXPLORE.exe 的程序杀死,重新 `net start mysql` MySQL 就愉快的重新启动了。
\ No newline at end of file
+打开任务管理器, IEXPLORE.exe 的程序杀死,重新 `net start mysql` MySQL 就愉快的重新启动了。
diff --git a/src/data/blog/2017-11-01.zookeeper-cluster.md b/src/data/blog/2017-11-01.zookeeper-cluster.md
new file mode 100644
index 00000000..5848972c
--- /dev/null
+++ b/src/data/blog/2017-11-01.zookeeper-cluster.md
@@ -0,0 +1,260 @@
+---
+title: "dubbo前置知识:zookeeper集群搭建"
+author: xkcoding
+pubDatetime: 2017-11-01T05:55:10.000Z
+slug: zookeeper-cluster
+featured: false
+draft: false
+tags:
+ - zookeeper
+ - dubbo
+ - linux
+description: "学 Dubbo 之前先把注册中心搭好。本文手把手教你在 3 台 Linux 虚拟机上搭建 Zookeeper 集群:从下载解压到配置 zoo.cfg,从创建 myid 文件到配置环境变量,每一步都有详细命令。最后验证集群状态,一个 leader 两个 follower,高可用注册中心就绑了。Dubbo 入门的前置必修课。"
+---
+
+拖了一天一天又一天,终于在时隔3月之后,又迎来了一篇新博客。真的是懒癌晚期了。o(╯□╰)o
+
+> `dubbo` 框架需要将服务统一注册到一个注册中心,才能被远程调用。这里注册中心可以使用 `zookeeper` 或者 `redis`。
+
+这里我们以 `zookeeper` 为例。`zookeeper` 可以单机版也就是 standalone模式运行,也可以使用集群方式运行。为了实现高可用,本例采用集群方式部署,需要保证至少半数以上的机器存活,所以至少为3台机器。话不多说,让我们开始 `zookeeper` 集群的搭建吧:
+
+
+
+1. 官网下载 `zookeeper-3.4.10.tar.gz` ,将软件包分别放在三台 Linux 虚拟机的 `/software` 目录下。
+2. 进入 `software` 目录下,解压软件包。
+
+ ```Shell
+ [root@localhost ~]# cd /software
+ [root@localhost software]# tar -zxvf zookeeper-3.4.10.tar.gz
+ [root@localhost software]# ll
+ total 34228
+ drwxr-xr-x. 10 1001 1001 4096 Mar 23 2017 zookeeper-3.4.10
+ -rw-r--r--. 1 root root 35042811 Nov 1 14:58 zookeeper-3.4.10.tar.gz
+ ```
+
+3. 解压后的文件夹名字包含了版本号,我们将其删除。
+
+ ```Shell
+ [root@localhost software]# mv zookeeper-3.4.10 zookeeper
+ [root@localhost software]# ll
+ total 34228
+ drwxr-xr-x. 10 1001 1001 4096 Mar 23 2017 zookeeper
+ -rw-r--r--. 1 root root 35042811 Nov 1 14:58 zookeeper-3.4.10.tar.gz
+ ```
+
+4. 进入刚解压的 `zookeeper` 的目录中
+
+ ```Shell
+ [root@bogon software]# cd zookeeper/
+ [root@bogon zookeeper]# ll
+ total 1580
+ drwxr-xr-x. 2 1001 1001 4096 Mar 23 2017 bin
+ -rw-rw-r--. 1 1001 1001 84725 Mar 23 2017 build.xml
+ drwxr-xr-x. 2 1001 1001 74 Mar 23 2017 conf
+ drwxr-xr-x. 10 1001 1001 4096 Mar 23 2017 contrib
+ drwxr-xr-x. 2 1001 1001 4096 Mar 23 2017 dist-maven
+ drwxr-xr-x. 6 1001 1001 4096 Mar 23 2017 docs
+ -rw-rw-r--. 1 1001 1001 1709 Mar 23 2017 ivysettings.xml
+ -rw-rw-r--. 1 1001 1001 5691 Mar 23 2017 ivy.xml
+ drwxr-xr-x. 4 1001 1001 4096 Mar 23 2017 lib
+ -rw-rw-r--. 1 1001 1001 11938 Mar 23 2017 LICENSE.txt
+ -rw-rw-r--. 1 1001 1001 3132 Mar 23 2017 NOTICE.txt
+ -rw-rw-r--. 1 1001 1001 1770 Mar 23 2017 README_packaging.txt
+ -rw-rw-r--. 1 1001 1001 1585 Mar 23 2017 README.txt
+ drwxr-xr-x. 5 1001 1001 44 Mar 23 2017 recipes
+ drwxr-xr-x. 8 1001 1001 4096 Mar 23 2017 src
+ -rw-rw-r--. 1 1001 1001 1456729 Mar 23 2017 zookeeper-3.4.10.jar
+ -rw-rw-r--. 1 1001 1001 819 Mar 23 2017 zookeeper-3.4.10.jar.asc
+ -rw-rw-r--. 1 1001 1001 33 Mar 23 2017 zookeeper-3.4.10.jar.md5
+ -rw-rw-r--. 1 1001 1001 41 Mar 23 2017 zookeeper-3.4.10.jar.sha1
+ ```
+
+ 解释一下,几个常见目录里的内容:
+ - `bin` 目录里存放的是一些可执行文件,基本以 `*.bat` 和 `*.sh` 结尾的文件。
+ - `conf` 目录里存放的是项目(软件)运行时的一些配置文件。
+ - `contrib` 目录存放的内容官方未提供的一些附加软件。
+ - `dist-maven` 目录存放的是 `zookeeper` 使用 `maven` 编译之后的源码和 jar文件
+ - `docs` 目录里存放的是一些文档
+ - `lib` 目录里存放的是依赖库
+ - `recipes` 目录下存放的是使用 `zookeeper` 实现的分布式锁、高并发、队列的一些方案
+ - `src` 目录里存放的是源码文件
+
+5. 在 `conf` 文件夹里对 `zookeeper` 进行配置,将示例配置文件复制并改名
+
+ ```Shell
+ [root@bogon software]# cd zookeeper/conf/
+ [root@bogon conf]# ll
+ total 12
+ -rw-rw-r--. 1 1001 1001 535 Mar 23 2017 configuration.xsl
+ -rw-rw-r--. 1 1001 1001 2161 Mar 23 2017 log4j.properties
+ -rw-rw-r--. 1 1001 1001 922 Mar 23 2017 zoo_sample.cfg
+ [root@bogon conf]# cp zoo_sample.cfg zoo.cfg
+ [root@bogon conf]# ll
+ total 16
+ -rw-rw-r--. 1 1001 1001 535 Mar 23 2017 configuration.xsl
+ -rw-rw-r--. 1 1001 1001 2161 Mar 23 2017 log4j.properties
+ -rw-r--r--. 1 root root 922 Nov 2 14:40 zoo.cfg
+ -rw-rw-r--. 1 1001 1001 922 Mar 23 2017 zoo_sample.cfg
+ [root@bogon conf]# vim zoo.cfg
+ ```
+
+ 然后在 `zoo.cfg` 里加上一些配置
+
+ ```bash
+ tickTime=2000
+ # The number of ticks that the initial
+ # synchronization phase can take
+ initLimit=10
+ # The number of ticks that can pass between
+ # sending a request and getting an acknowledgement
+ syncLimit=5
+ # the directory where the snapshot is stored.
+ # do not use /tmp for storage, /tmp here is just
+ # example sakes.
+
+ ################ 修改的地方1 ################
+ # dataDir=/tmp/zookeeper
+ # 将原先的 dataDir 注释,并修改成自定义的路径
+ dataDir=/software/zookeeper/data
+
+ # the port at which the clients will connect
+ clientPort=2181
+ # the maximum number of client connections.
+ # increase this if you need to handle more clients
+ #maxClientCnxns=60
+ #
+ # Be sure to read the maintenance section of the
+ # administrator guide before turning on autopurge.
+ #
+ # http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance
+ #
+ # The number of snapshots to retain in dataDir
+ #autopurge.snapRetainCount=3
+ # Purge task interval in hours
+ # Set to "0" to disable auto purge feature
+ #autopurge.purgeInterval=1
+
+ ################ 修改的地方2 ################
+ # 在文件的末尾加上
+ server.0=172.16.15.129:2888:3888
+ server.1=172.16.15.130:2888:3888
+ server.2=172.16.15.131:2888:3888
+ ```
+
+6. 新建配置文件中的 `dataDir` 配置的目录
+
+ ```Shell
+ [root@bogon conf]# cd ../
+ [root@bogon zookeeper]# mkdir data
+ [root@bogon zookeeper]# ll
+ total 1580
+ drwxr-xr-x. 2 1001 1001 4096 Mar 23 2017 bin
+ -rw-rw-r--. 1 1001 1001 84725 Mar 23 2017 build.xml
+ drwxr-xr-x. 2 1001 1001 88 Nov 2 14:57 conf
+ drwxr-xr-x. 10 1001 1001 4096 Mar 23 2017 contrib
+ drwxr-xr-x. 2 root root 6 Nov 2 14:58 data
+ drwxr-xr-x. 2 1001 1001 4096 Mar 23 2017 dist-maven
+ drwxr-xr-x. 6 1001 1001 4096 Mar 23 2017 docs
+ -rw-rw-r--. 1 1001 1001 1709 Mar 23 2017 ivysettings.xml
+ -rw-rw-r--. 1 1001 1001 5691 Mar 23 2017 ivy.xml
+ drwxr-xr-x. 4 1001 1001 4096 Mar 23 2017 lib
+ -rw-rw-r--. 1 1001 1001 11938 Mar 23 2017 LICENSE.txt
+ -rw-rw-r--. 1 1001 1001 3132 Mar 23 2017 NOTICE.txt
+ -rw-rw-r--. 1 1001 1001 1770 Mar 23 2017 README_packaging.txt
+ -rw-rw-r--. 1 1001 1001 1585 Mar 23 2017 README.txt
+ drwxr-xr-x. 5 1001 1001 44 Mar 23 2017 recipes
+ drwxr-xr-x. 8 1001 1001 4096 Mar 23 2017 src
+ -rw-rw-r--. 1 1001 1001 1456729 Mar 23 2017 zookeeper-3.4.10.jar
+ -rw-rw-r--. 1 1001 1001 819 Mar 23 2017 zookeeper-3.4.10.jar.asc
+ -rw-rw-r--. 1 1001 1001 33 Mar 23 2017 zookeeper-3.4.10.jar.md5
+ -rw-rw-r--. 1 1001 1001 41 Mar 23 2017 zookeeper-3.4.10.jar.sha1
+ ```
+
+ 在 `data` 目录中新建一个 `myid` 文件,这个文件中的内容存放配置文件中对应 IP 的 `serverId`。
+
+ ```Shell
+ [root@bogon zookeeper]# cd data
+ [root@bogon data]# vim myid
+ [root@bogon data]# cat myid
+ 0
+ ```
+
+7. 将 `zookeeper` 的目录配置在 Linux 系统的环境变量中
+
+ ```Shell
+ [root@bogon data]# vim /etc/profile
+ ```
+
+ 在文件末尾添加
+
+ ```bash
+ export ZOOKEEPER_HOME=/software/zookeeper
+ export PATH=$ZOOKEEPER_HOME/bin:$PATH
+ ```
+
+ 然后让环境变量生效
+
+ ```Shell
+ [root@bogon data]# source /etc/profile
+ ```
+
+8. 将另外两台虚拟机也按照 `2-7` 的步骤配置,注意第6步的 `myid` 文件里存放的内容,应该与 `zoo.cfg` 配置文件末尾的 `server.n=ip:2888:3888` 的 `n` 对应
+9. 配置完环境变量之后,我们就可以不需要到 `bin` 目录下,直接在任意目录下执行 zookeeper 中 bin 目录下的文件了。
+ - `zkServer.sh status` :查看 `zookeeper` 的状态
+ - `zkServer.sh start` :启动 `zookeeper`
+ - `zkServer.sh stop` :关闭 `zookeeper`
+
+10. 分别查看各个虚拟机的启动情况,以及启动后的状态。
+
+虚拟机1:(follower)
+
+```Shell
+[root@bogon software]# zkServer.sh status
+ZooKeeper JMX enabled by default
+Using config: /software/zookeeper/bin/../conf/zoo.cfg
+Error contacting service. It is probably not running.
+[root@bogon software]# zkServer.sh start
+ZooKeeper JMX enabled by default
+Using config: /software/zookeeper/bin/../conf/zoo.cfg
+Starting zookeeper ... STARTED
+[root@bogon software]# zkServer.sh status
+ZooKeeper JMX enabled by default
+Using config: /software/zookeeper/bin/../conf/zoo.cfg
+Mode: follower
+```
+
+虚拟机2:(leader)
+
+```Shell
+[root@bogon ~]# zkServer.sh status
+ZooKeeper JMX enabled by default
+Using config: /software/zookeeper/bin/../conf/zoo.cfg
+Error contacting service. It is probably not running.
+[root@bogon ~]# zkServer.sh start
+ZooKeeper JMX enabled by default
+Using config: /software/zookeeper/bin/../conf/zoo.cfg
+Starting zookeeper ... STARTED
+[root@bogon ~]# zkServer.sh status
+ZooKeeper JMX enabled by default
+Using config: /software/zookeeper/bin/../conf/zoo.cfg
+Mode: leader
+```
+
+虚拟机3:(follower)
+
+```Shell
+[root@bogon ~]# zkServer.sh status
+ZooKeeper JMX enabled by default
+Using config: /software/zookeeper/bin/../conf/zoo.cfg
+Error contacting service. It is probably not running.
+[root@bogon ~]# zkServer.sh start
+ZooKeeper JMX enabled by default
+Using config: /software/zookeeper/bin/../conf/zoo.cfg
+Starting zookeeper ... STARTED
+[root@bogon ~]# zkServer.sh status
+ZooKeeper JMX enabled by default
+Using config: /software/zookeeper/bin/../conf/zoo.cfg
+Mode: follower
+```
+
+11. 至此,`zookeeper` 集群的简单搭建已经正式结束了。当然还有一些个性化(调优)的配置参数,请自行参照 `zookeeper` 的[官方文档](http://zookeeper.apache.org/doc/r3.4.10/zookeeperStarted.html) 在 `zoo.cfg` 中配置。
diff --git a/source/_posts/2017-11-08.dubbo-admin-install.md b/src/data/blog/2017-11-08.dubbo-admin-install.md
similarity index 87%
rename from source/_posts/2017-11-08.dubbo-admin-install.md
rename to src/data/blog/2017-11-08.dubbo-admin-install.md
index 600b5b65..0a077c72 100644
--- a/source/_posts/2017-11-08.dubbo-admin-install.md
+++ b/src/data/blog/2017-11-08.dubbo-admin-install.md
@@ -1,22 +1,19 @@
---
-title: dubbo前置知识:dubbo-admin 的安装
-comments: true
-fancybox: true
-toc: false
-date: 2017-11-08 15:37:14
+title: "dubbo前置知识:dubbo-admin 的安装"
+author: xkcoding
+pubDatetime: 2017-11-08T07:37:14.000Z
+slug: dubbo-admin-install
+featured: false
+draft: false
tags:
-- dubbo
-- java
-categories:
-- 技术相关
-- java
-description:
-permalink: dubbo-admin-install
-top:
+ - dubbo
+ - java
+description: "Dubbo 服务部署后如何监控和治理?dubbo-admin 是官方提供的可视化管控台。本文详细记录从 GitHub 拉取源码、Maven 打包、Tomcat 部署到配置 Zookeeper 集群地址的完整流程。重点讲解端口冲突避免、JVM 参数调优等实战技巧,让你的 Dubbo 服务治理一目了然。"
---
+
> `dubbo-admin` 是 `dubbo` 官方提供的一个的管控台程序,可以对 `dubbo` 服务进行**监控**,还可以对其进行**权重分配**、**负载均衡策略**等的调优操作,简而言之,我们可以通过 `dubbo-admin`,对我们的 `dubbo` 服务进行服务治理及服务监控。
-
+
现在,我们开始安装及配置 `dubbo-admin`。
@@ -64,8 +61,7 @@ top:
5. 前往[ Apache 的 Tomcat 主页](https://tomcat.apache.org/download-90.cgi),下载并解压一个新的 `Tomcat` 程序,专门用来启动 `dubbo-admin` 。
6. 接下来我们开始对 `Tomcat` 以及 `dubbo-admin` 做一些配置。
-
- 1. 可以直接将打包出来的 war 文件放在 `webapps` 目录下,但是为了后面访问 URL 的时候更简单(*其实就是想偷懒*)可以将其放入 `ROOT` 下,删除 `webapps/ROOT` 目录下的所有文件,然后将 打包的 `dubbo-admin.war` 程序解压至 `webapps/ROOT` 目录下,注意不需要 dubbo-admin 这个目录,只要将里面的 WEB-INF 这级目录及其子目录的文件放置在 `webapps/ROOT` 中。
+ 1. 可以直接将打包出来的 war 文件放在 `webapps` 目录下,但是为了后面访问 URL 的时候更简单(_其实就是想偷懒_)可以将其放入 `ROOT` 下,删除 `webapps/ROOT` 目录下的所有文件,然后将 打包的 `dubbo-admin.war` 程序解压至 `webapps/ROOT` 目录下,注意不需要 dubbo-admin 这个目录,只要将里面的 WEB-INF 这级目录及其子目录的文件放置在 `webapps/ROOT` 中。
2. 配置 `dubbo-admin` 的一些启动配置,配置文件是 `WEB-INF/dubbo.properties` ,我们需要把之前的 `zookeeper` 集群地址配置上。
@@ -81,7 +77,7 @@ top:
```
3. 因为考虑到我们自己写程序可能会占用`8080`端口,因此我们对这个专门为 `dubbo-admin` 的 `Tomcat` 重新配置端口,配置文件是 `tomcat/conf/server.xml`。
- - 修改 **HTTP** 端口
+ - 修改 **HTTP** 端口
```xml
-
+
```
4. 因为 `dubbo-admin` 程序较大,因此需要对 `Tomcat` 做一些调优准备, 在 `tomcat/bin/catalina.sh` 文件中的第二行加入 `JAVA_OPTS='-Xms256m -Xmx512m'` ,如下所示。
@@ -125,4 +121,4 @@ top:
- 系统日志

- 系统环境
- 
\ No newline at end of file
+ 
diff --git a/source/_posts/2017-11-10.druid-oracle-pscache.md b/src/data/blog/2017-11-10.druid-oracle-pscache.md
similarity index 68%
rename from source/_posts/2017-11-10.druid-oracle-pscache.md
rename to src/data/blog/2017-11-10.druid-oracle-pscache.md
index 12549acf..7514a80f 100644
--- a/source/_posts/2017-11-10.druid-oracle-pscache.md
+++ b/src/data/blog/2017-11-10.druid-oracle-pscache.md
@@ -1,19 +1,16 @@
---
-title: 使用 Druid 加速 oracle 的查询
-comments: true
-fancybox: false
-toc: false
-date: 2017-11-10 09:06:33
+title: "使用 Druid 加速 oracle 的查询"
+author: xkcoding
+pubDatetime: 2017-11-10T01:06:33.000Z
+slug: druid-oracle-pscache
+featured: false
+draft: false
tags:
-- oracle
-- java
-categories:
-- 技术相关
-- oracle
-description:
-permalink: druid-oracle-pscache
-top:
+ - oracle
+ - java
+description: "Oracle 查询慢不一定是 SQL 的问题。开启 Druid 的 PSCache 功能后,性能提升了 20%!原理是 Oracle 支持游标,PreparedStatement 被缓存复用时服务端游标不关闭,性能提升可达一个数量级。关键是 Druid 是唯一解决了 PSCache 内存占用过多问题的连接池。"
---
+
##1. 问题
最近,公司的 web 应用,查询数据有些慢,数据库使用的是 oracle,连接池采用了阿里巴巴温少开源的 Druid 连接池,于是开始寻找解决办法。
@@ -29,4 +26,4 @@ top:
oracle 支持游标,一个 **PreparedStatement** 对应服务器一个游标,如果 **PreparedStatement** 被缓存起来重复执行,**PreparedStatement** 没有被关闭,服务器端的游标就不会被关闭,性能提高非常显著。在类似 `SELECT * FROM T WHERE ID = ? `这样的场景,启用 PSCache 和不启用 PSCache 的性能可能是相差一个数量级的。
-连接 oracle 数据库,打开 **PSCache**,但是其他的数据库连接池都会存在内存占用过多的问题,Druid 是唯一解决这个问题的连接池。
\ No newline at end of file
+连接 oracle 数据库,打开 **PSCache**,但是其他的数据库连接池都会存在内存占用过多的问题,Druid 是唯一解决这个问题的连接池。
diff --git a/source/_posts/2017-11-14.mysql-chinese-character-encoding.md b/src/data/blog/2017-11-14.mysql-chinese-character-encoding.md
similarity index 89%
rename from source/_posts/2017-11-14.mysql-chinese-character-encoding.md
rename to src/data/blog/2017-11-14.mysql-chinese-character-encoding.md
index 3397d35d..e49ff73c 100644
--- a/source/_posts/2017-11-14.mysql-chinese-character-encoding.md
+++ b/src/data/blog/2017-11-14.mysql-chinese-character-encoding.md
@@ -1,19 +1,16 @@
---
-title: Mac 下彻底解决 mysql 中文乱码问题
-comments: true
-fancybox: true
-toc: true
-date: 2017-11-14 09:26:08
+title: "Mac 下彻底解决 mysql 中文乱码问题"
+author: xkcoding
+pubDatetime: 2017-11-14T01:26:08.000Z
+slug: mysql-chinese-character-encoding
+featured: false
+draft: false
tags:
-- mysql
-- Mac
-categories:
-- 技术相关
-- mysql
-description:
-permalink: mysql-chinese-character-encoding
-top:
+ - mysql
+ - mac
+description: "MySQL 中文乱码?别急着改 my.cnf!本文梳理了 Mac 环境下 MySQL 乱码的 4 种原因:程序配置、数据库/表/字段编码、终端设置、Navicat 连接设置。特别是 Homebrew 安装的 MySQL 默认没有 my.cnf,以及 Navicat 的 Encoding 要选 Auto 而非 UTF-8 这两个坑,踩过才知道痛。"
---
+
数据库中文乱码是个比较常见的问题,今天笔者就遇到了。
> 系统环境:macOS Sierra 10.12.6 MySQL 版本:5.7.18 Homebrew 连接软件:Navicat Premium 12.0.12 TNT edition
@@ -46,7 +43,6 @@ mysql> show variables like '%char%';
```
1. 与客户端(网页或软件:web/终端/数据库管理工具)有关的:
-
- character_set_client:对客户端的请求字符串进行编码
- character_set_connection:连接服务器时的默认编码,例如navicat连接阿里云服务器
- character_set_results:表示客户端想以哪种编码接收查询结果
@@ -54,14 +50,12 @@ mysql> show variables like '%char%';
如果后两个没有特别指定,则默认和character_set_client是一样的
2. 与服务器端(增删查改命令)有关的:
-
- character_set_server:服务器编码
- character_set_database:创建数据库时的默认编码
如果后一个没有特别指定,则默认和character_set_server是一样的
3. 其他:
-
- character_set_filesystem:以二进制文件进行设置,不影响字符显示
- character_set_system:用于mysql存储标识符,一直是UTF8,也不影响字符显示
@@ -157,4 +151,3 @@ export $LANG;
## 5. 参考
[Mac系统解决MySQL乱码问题](http://www.siguoya.name/pc/home/article/235)
-
diff --git a/src/data/blog/2017-12-05.redux-and-mobx-first.md b/src/data/blog/2017-12-05.redux-and-mobx-first.md
new file mode 100644
index 00000000..ffa472ca
--- /dev/null
+++ b/src/data/blog/2017-12-05.redux-and-mobx-first.md
@@ -0,0 +1,68 @@
+---
+title: "redux 和 mobx 初体验"
+author: xkcoding
+pubDatetime: 2017-12-05T09:58:56.000Z
+slug: redux-and-mobx-first
+featured: false
+draft: false
+tags:
+ - react
+description: "前段时间搞了 Vue,现在又得开始写 React 了。 我堂堂的一个后端,真是太残忍啦 o(╯□╰)o 接触过 Vue 的应该都知道,Vue 的状态管理可以有两种实现,一种是 Vuex,还有一种是 eventBus 形式。 React 状态管理,老牌的应该是 Redux,但是存在了太多的概念,对于刚接触的童鞋不太友好。mobx 当属时代的新星了。不管从效率,还是易读性都远超 Redux。..."
+---
+
+前段时间搞了 Vue,现在又得开始写 React 了。 我堂堂的一个后端,真是太残忍啦 o(╯□╰)o
+
+接触过 Vue 的应该都知道,Vue 的状态管理可以有两种实现,一种是 Vuex,还有一种是 eventBus 形式。
+
+React 状态管理,老牌的应该是 Redux,但是存在了太多的概念,对于刚接触的童鞋不太友好。mobx 当属时代的新星了。不管从效率,还是易读性都远超 Redux。
+
+下面带来俩小🌰大家感受下~~
+
+
+
+#### 最简单的一个 redux 操作
+
+```js
+import { createStore } from "redux";
+
+const ADD_ACTION = "ADD";
+
+const add = num => {
+ return {
+ type: ADD_ACTION,
+ num,
+ };
+};
+
+const initialState = {
+ count: 0,
+};
+
+const reducers = (state = initialState, action) => {
+ switch (action.type) {
+ case ADD_ACTION:
+ return Object.assign({}, state, { count: state.count + action.num });
+ default:
+ return state;
+ }
+};
+
+const reduxStore = createStore(reducers);
+
+reduxStore.dispatch(add(1));
+```
+
+#### 最简单的一个 mobx 操作
+
+```js
+import { observable, action } from "mobx";
+
+const mobxStore = observable({
+ count: 0,
+ add: action(num => {
+ this.count += num;
+ }),
+});
+
+mobxStore.add(1);
+```
diff --git a/source/_posts/2017-12-11.tips-for-improve-java-skill.md b/src/data/blog/2017-12-11.tips-for-improve-java-skill.md
similarity index 74%
rename from source/_posts/2017-12-11.tips-for-improve-java-skill.md
rename to src/data/blog/2017-12-11.tips-for-improve-java-skill.md
index 3e1013a3..5be856fd 100644
--- a/source/_posts/2017-12-11.tips-for-improve-java-skill.md
+++ b/src/data/blog/2017-12-11.tips-for-improve-java-skill.md
@@ -1,18 +1,15 @@
---
-title: 提升 JAVA 编程技能
-comments: true
-fancybox: false
-toc: false
-date: 2017-12-11 13:18:56
+title: "提升 JAVA 编程技能"
+author: xkcoding
+pubDatetime: 2017-12-11T05:18:56.000Z
+slug: tips-for-improve-java-skill
+featured: false
+draft: false
tags:
-- 翻译
-categories:
-- 技术相关
-- 翻译
-description:
-permalink: tips-for-improve-java-skill
-top:
+ - 技术翻译
+description: "翻译自: How to improve Java programming skill"
---
+
翻译自: [How to improve Java programming skill](http://www.javatechblog.com/java/how-to-improve-java-programming-skill/)
@@ -27,4 +24,4 @@ top:
8. 多阅读大牛的技术博客
9. 保持跟进新的 JAVA 技术,可以不去精通,但是你不能落后
10. 多阅读文档
-11. 不要去和他人比较(最后一点,但并不是关键性的),最大的敌人永远是你自己
\ No newline at end of file
+11. 不要去和他人比较(最后一点,但并不是关键性的),最大的敌人永远是你自己
diff --git a/source/_posts/2018-01-03.devops-gitlab.md b/src/data/blog/2018-01-03.devops-gitlab.md
similarity index 85%
rename from source/_posts/2018-01-03.devops-gitlab.md
rename to src/data/blog/2018-01-03.devops-gitlab.md
index 7f0656f6..f11b3476 100644
--- a/source/_posts/2018-01-03.devops-gitlab.md
+++ b/src/data/blog/2018-01-03.devops-gitlab.md
@@ -1,19 +1,15 @@
---
-title: DevOps 之 GitLab 安装、汉化及常见错误处理
-comments: true
-fancybox: true
-toc: true
-date: 2018-01-03 11:12:37
+title: "DevOps 之 GitLab 安装、汉化及常见错误处理"
+author: xkcoding
+pubDatetime: 2018-01-03T03:12:37.000Z
+slug: devops-gitlab
+featured: false
+draft: false
tags:
-- gitlab
-- devops
-categories:
-- 技术相关
-- devops
-description:
-permalink: devops-gitlab
-top:
+ - devops
+description: "搭建企业私有 Git 仓库,GitLab 是首选。本文涵盖 CentOS 下的完整部署流程:从依赖安装、镜像源配置、QQ 邮箱 SMTP 设置,到中文汉化包的正确使用姿势。还有两个必踩的坑:reconfigure 卡住和端口占用,以及配置 push 时邮件通知 diff 信息的完整方案。"
---
+
公司现在开始实行 DevOps 的开发模式,代码托管采用了 GitLab 搭建私有代码仓库,本文记录了 GitLab 的安装、汉化及常见错误处理。
@@ -135,13 +131,11 @@ gitlab-ctl start|stop|status|restart
2. 我出现这个的错误是因为日志中出现了端口占用
- ```shell
- ==> /var/log/gitlab/unicorn/unicorn_stderr.log <==
-
- Errno::EADDRINUSE: Address already in use - bind(2) for 127.0.0.1:8080
- ```
-
+ ```shell
+ ==> /var/log/gitlab/unicorn/unicorn_stderr.log <==
+ Errno::EADDRINUSE: Address already in use - bind(2) for 127.0.0.1:8080
+ ```
3. `vim /etc/gitlab/gitlab.rb` 去修改 `unicorn` 的 url 和 port
@@ -165,7 +159,7 @@ gitlab-ctl start|stop|status|restart
1. 创建用户的时候,邮箱保证正确。
-2. User Settings --> Profile --> Main Settings --> email / public email 里是你的邮箱
+2. User Settings --> Profile --> Main Settings --> email / public email 里是你的邮箱
3. User Settings --> Emails 保证第二步里显示的 email在这里是 verified 状态
@@ -179,4 +173,4 @@ gitlab-ctl start|stop|status|restart

-7. ❤️enjoy it ~~
\ No newline at end of file
+7. ❤️enjoy it ~~
diff --git a/src/data/blog/2018-01-04.devops-jenkins.md b/src/data/blog/2018-01-04.devops-jenkins.md
new file mode 100644
index 00000000..811adf20
--- /dev/null
+++ b/src/data/blog/2018-01-04.devops-jenkins.md
@@ -0,0 +1,260 @@
+---
+title: "DevOps 之 Jenkins 安装、配置、美化、插件及常见错误处理"
+author: xkcoding
+pubDatetime: 2018-01-04T05:19:13.000Z
+slug: devops-jenkins
+featured: false
+draft: false
+tags:
+ - devops
+description: "GitLab 搭好了,下一步就是 Jenkins 持续集成。本文记录完整的 Jenkins 部署实战:JDK 路径配置、阿里云 Maven 镜像加速、个性化邮件模板(含完整 HTML 代码)。还推荐了 Material 风格主题美化方案,以及 11 个必装插件:Gitlab Hook、FireLine 代码规约检查、钉钉通知等。"
+---
+
+继续上一篇的话题,既然已经搭建了 GitLab 的代码仓库,那么现在就可以开始进行下一步持续集成环境的搭建了。公司准备利用 Jenkins CI 进行持续集成,本文记录了 Jenkins 的安装、基础配置、界面美化、常用插件及常见错误处理。
+
+
+
+## 1. 安装Jenkins
+
+**准备工作**
+
+1. 安装 JDK,并配置环境变量
+
+2. 安装 maven
+
+3. 配置阿里云的 maven 镜像源地址,`vim /usr/share/maven/conf/settings.xml` 将👇内容添加进 `mirrors` 节点
+
+ ```xml
+
+
+ nexus-aliyun
+ *
+ Nexus aliyun
+ http://maven.aliyun.com/nexus/content/groups/public
+
+ ```
+
+4. 安装 git
+
+**配置镜像源地址**
+
+```shell
+sudo wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat-stable/jenkins.repo
+sudo rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io.key
+yum install jenkins
+```
+
+**修改访问 IP 和 端口号**
+
+1. `vim /etc/sysconfig/jenkins`
+2. `JENKINS_PORT="10080"`
+
+**配置 JDK 路径到 Jenkins 中**
+
+1. `vim /etc/init.d/jenkins`
+
+2. 将自己的 java 目录加入到 `candidates` 里
+
+ ```properties
+ candidates="
+ /etc/alternatives/java
+ /usr/lib/jvm/java-1.8.0/bin/java
+ /usr/lib/jvm/jre-1.8.0/bin/java
+ /usr/lib/jvm/java-1.7.0/bin/java
+ /usr/lib/jvm/jre-1.7.0/bin/java
+ /usr/bin/java
+ /opt/jdk1.8.0_144/bin/java
+ "
+ ```
+
+**安装完毕后启动**
+
+```shell
+sudo service jenkins start # 启动jenkins服务
+```
+
+## 2. 配置 Jenkins
+
+1. **初始化密码**
+
+ `cat /var/lib/jenkins/secrets/initialAdminPassword`
+
+2. **安装插件**
+
+ 选择左侧推荐的插件安装即可
+
+3. **配置管理员账号密码**
+
+4. **配置 JDK、Maven、Git、Docker**
+
+ 系统管理 → 全局工具配置(尽量在服务器本地安装,然后输入安装路径,不推荐使用在线安装,因为在线安装有些资源都在国外,下载速度极慢)
+
+5. **配置邮件服务器信息**
+
+ 系统管理 → 系统设置,①Jenkins Location→系统管理员邮件地址②Extended E-mail Notification③邮件通知。
+
+ 其中**Extended E-mail Notification**的两个地方可以做个性化配置:
+ - Default Content Type 邮件的内容类型,改为`HTML(text/html)`。
+
+ - Default Subject 邮件的标题,改为`构建通知:$PROJECT_NAME - Build # $BUILD_NUMBER - $BUILD_STATUS!`。
+
+ - Default Content 邮件的内容,改为👇内容。
+
+ ```html
+
+
+
+
+ ${PROJECT_NAME}-第${BUILD_NUMBER}次构建日志
+
+
+
+
+
+ (本邮件是程序自动下发的,请勿回复!)
+
+
+
+
+ 构建结果 - ${BUILD_STATUS}
+
+
+
+
+
+
+ 构建信息
+
+
+
+
+
+
+
+
+
+
+ Changes Since Last Successful Build:
+
+
+
+
+
+
+ ${CHANGES_SINCE_LAST_SUCCESS,reverse=true, format="Changes for
+ Build #%n: %c ",showPaths=true,changesFormat="
+ [%a] %m
+ ",pathFormat=" %p"}
+
+
+
+
+ Test Informations
+
+
+
+
+
+
+ Total:${TEST_COUNTS,var="total"},Pass:${TEST_COUNTS,var="pass"},Failed:${TEST_COUNTS,var="fail"},Skiped:${TEST_COUNTS,var="skip"}
+
+
+
+
+
+ 构建日志 (最后 100行):
+
+
+
+
+
+
+
+
+
+
+
+ ```
+
+ - 勾选上 **Enable Debug Mode、Require Administrator for Template Testing、Enable watching for jobs、Allow sending to unregistered users**
+
+## 3. 美化 Jenkins
+
+安利一款material风格的主题:http://afonsof.com/jenkins-material-theme/ ,支持使用 CDN,也支持自定义公司 logo 等等。
+
+## 4. 常用插件
+
+1. Maven Integration plugin:用于构建 Maven 项目
+2. Gitlab Hook Plugin:用于触发 GitLab 的一些 WebHooks 来构建项目
+3. Simple Theme Plugin:配合上面的美化 Jenkins 使用
+4. FireLine Plugin:代码规范检查,已经集成了阿里巴巴的代码规约(P3C)检查
+5. HTML Publisher plugin:将一些测试结果变成 HTML 文档
+6. JUnit Plugin:JUnit 测试
+7. [Checkstyle Plug-in](https://plugins.jenkins.io/checkstyle):检测代码风格
+8. [FindBugs Plug-in](https://plugins.jenkins.io/findbugs):检测构建出来的 class 文件是否安全
+9. [Static Analysis Collector Plug-in](https://plugins.jenkins.io/analysis-collector):对7、8两个插件检测出来的结果进行汇总
+10. Extended E-mail Notification:个性化邮件模板
+11. [Dingding[钉钉] Plugin](https://wiki.jenkins.io/display/JENKINS/Dingding+Notification+Plugin):钉钉通知插件
+
+## 5. 常见错误解决
+
+**添加镜像源的时候出现如下错误:**
+
+```shell
+Resolving pkg.jenkins-ci.org (pkg.jenkins-ci.org)... failed: Temporary failure in name resolution.
+
+wget: unable to resolve host address ‘pkg.jenkins-ci.org’
+```
+
+解决方法:
+
+`vim /etc/resolv.conf` 添加一行 `nameserver 8.8.8.8`
+
+**HTML Publisher Plugin 插件在新的Jenkins版本中,打开生成的网页中无法加载CSS、无法点击按钮等情况:**
+
+由于 CSP(Content Security Policy)所致,可在 系统管理→脚本命令行 中执行:
+
+`System.setProperty("hudson.model.DirectoryBrowserSupport.CSP","")`
diff --git a/source/_posts/2018-01-11.camelcase-underline-transform.md b/src/data/blog/2018-01-11.camelcase-underline-transform.md
similarity index 79%
rename from source/_posts/2018-01-11.camelcase-underline-transform.md
rename to src/data/blog/2018-01-11.camelcase-underline-transform.md
index f8e0e094..d973473e 100644
--- a/source/_posts/2018-01-11.camelcase-underline-transform.md
+++ b/src/data/blog/2018-01-11.camelcase-underline-transform.md
@@ -1,19 +1,16 @@
---
-title: 驼峰命名和下划线命名互相转化
-comments: true
-fancybox: false
-toc: false
-date: 2018-01-11 09:47:10
+title: "驼峰命名和下划线命名互相转化"
+author: xkcoding
+pubDatetime: 2018-01-11T01:47:10.000Z
+slug: camelcase-underline-transform
+featured: false
+draft: false
tags:
-- 代码片段
-- java
-categories:
-- 代码片段
-- java
-description: 分享一个驼峰命名和下划线命名互相转化的工具类
-permalink: camelcase-underline-transform
-top:
+ - 代码片段
+ - java
+description: "数据库字段是 user_name,Java 属性是 userName,来回转换烦不烦?这个工具类帮你搞定驼峰命名和下划线命名的互转。基于正则表达式实现,代码简洁高效,直接复制到项目里就能用。ORM 框架没覆盖的场景,手动转换必备。"
---
+
```java
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -74,4 +71,3 @@ public class CasesUtil {
}
}
```
-
diff --git a/source/_posts/2018-01-12.elasticsearch-note.md b/src/data/blog/2018-01-12.elasticsearch-note.md
similarity index 78%
rename from source/_posts/2018-01-12.elasticsearch-note.md
rename to src/data/blog/2018-01-12.elasticsearch-note.md
index dc11c1e1..4b539052 100644
--- a/source/_posts/2018-01-12.elasticsearch-note.md
+++ b/src/data/blog/2018-01-12.elasticsearch-note.md
@@ -1,19 +1,16 @@
---
-title: ElasticSearch 笔记
-comments: true
-fancybox: true
-toc: true
-date: 2018-01-12 17:11:18
+title: "ElasticSearch 笔记"
+author: xkcoding
+pubDatetime: 2018-01-12T09:11:18.000Z
+slug: elasticsearch-note
+featured: false
+draft: false
tags:
-- elasticsearch
-- java
-categories:
-- 技术相关
-- elasticsearch
-description:
-permalink: elasticsearch_note
-top:
+ - elasticsearch
+ - java
+description: "从零到一的 ElasticSearch 完整学习笔记!覆盖单机安装、分布式集群配置、head 插件和 ik 中文分词插件安装。详解索引、类型、文档、分片等核心概念,附带大量 RESTful API 示例:条件查询、聚合查询、复合查询。最后用 Spring Boot 整合 ES,实现增删改查和复杂搜索功能。"
---
+
`ElasticSearch`是一个基于 `Lucene` 的分布式搜索引擎,业内简称ES。它提供了基于 `RESTful` 风格的全文搜索API。`Elasticsearch` 是用 `Java` 开发的,并作为 `Apache` 许可条款下的开放源码发布,是当前最流行的企业级搜索引擎。另外,它的分布式设计让它天生就适合用于云计算中,并能够达到准实时搜索,而且安装使用方便,还拥有稳定,可靠,快速等特性。本文是我对 ES 的一个完整记录,方便后期查阅。另外大家还可以查阅更多的相关资料对 `ElasticSearch` 有更深入的了解。
@@ -153,6 +150,7 @@ top:
```
> 如果觉得这一步的依赖下载比较慢的话,有两种解决方式:
+ >
> 1. 可以选择科学上网
> 2. 使用国内阿里的镜像源,使用 cnpm 安装,具体如何配置,本文不做赘述。具体参考:http://npm.taobao.org/
@@ -197,11 +195,11 @@ top:
./bin/plugin install mobz/elasticsearch-head
```
-2. 打开浏览器,访问 http://localhost:9200/_plugin/head/ ,查看 head 插件运行效果
+2. 打开浏览器,访问 http://localhost:9200/\_plugin/head/ ,查看 head 插件运行效果
#### 1.3.2. ik 中文分词插件
-> ik 分词插件版本与ES版本对照,请查阅:https://github.com/medcl/elasticsearch-analysis-ik#versions
+> ik 分词插件版本与ES版本对照,请查阅:https://github.com/medcl/elasticsearch-analysis-ik#versions
##### 手动安装
@@ -211,12 +209,12 @@ top:
cd ./plugins && mkdir ik
```
-2. 下载编译包到 ik 目录,下载地址:https://github.com/medcl/elasticsearch-analysis-ik/releases
+2. 下载编译包到 ik 目录,下载地址:https://github.com/medcl/elasticsearch-analysis-ik/releases
3. 解压下载的压缩包
```shell
- unzip elasticsearch-analysis-ik-{ ES 版本 }.zip
+ unzip elasticsearch-analysis-ik-{ ES 版本 }.zip
```
4. 重启 ES
@@ -231,7 +229,7 @@ top:
2. 重启 ES
-------
+---
## 2. 常见错误
@@ -246,7 +244,7 @@ vm.max_map_count:限制一个进程可以拥有的VMA(虚拟内存区域)的
切换到root用户,修改配置文件:
```bash
-vim /etc/sysctl.conf
+vim /etc/sysctl.conf
```
添加下面的内容:
@@ -349,33 +347,33 @@ API 请求方式:GET、POST、PUT、DELETE
```json
{
- "settings":{
- "number_of_shards":3,
- "number_of_replicas":1
- },
- "mappings":{
- "person":{
- "properties":{
- "name":{
- "type":"text"
- },
- "age":{
- "type":"integer"
- },
- "country":{
- "type":"keyword"
- },
- "birthday":{
- "type":"date",
- "format":"yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
- }
- }
- }
- }
+ "settings": {
+ "number_of_shards": 3,
+ "number_of_replicas": 1
+ },
+ "mappings": {
+ "person": {
+ "properties": {
+ "name": {
+ "type": "text"
+ },
+ "age": {
+ "type": "integer"
+ },
+ "country": {
+ "type": "keyword"
+ },
+ "birthday": {
+ "type": "date",
+ "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
+ }
+ }
+ }
+ }
}
```
-{% note success %}
+{% note success %}
**json 请求体字段解释:**
@@ -392,9 +390,9 @@ API 请求方式:GET、POST、PUT、DELETE
```json
{
- "acknowledged": true,
- "shards_acknowledged": true,
- "index": "people"
+ "acknowledged": true,
+ "shards_acknowledged": true,
+ "index": "people"
}
```
@@ -410,7 +408,7 @@ API 请求方式:GET、POST、PUT、DELETE
```json
{
- "acknowledged": true
+ "acknowledged": true
}
```
@@ -426,10 +424,10 @@ API 请求方式:GET、POST、PUT、DELETE
```json
{
- "name":"xkcoding",
- "age":24,
- "country":"中国",
- "birthday":"1994-11-22"
+ "name": "xkcoding",
+ "age": 24,
+ "country": "中国",
+ "birthday": "1994-11-22"
}
```
@@ -437,18 +435,18 @@ API 请求方式:GET、POST、PUT、DELETE
```json
{
- "_index": "people",
- "_type": "person",
- "_id": "1",
- "_version": 1,
- "result": "created",
- "_shards": {
- "total": 2,
- "successful": 2,
- "failed": 0
- },
- "_seq_no": 0,
- "_primary_term": 1
+ "_index": "people",
+ "_type": "person",
+ "_id": "1",
+ "_version": 1,
+ "result": "created",
+ "_shards": {
+ "total": 2,
+ "successful": 2,
+ "failed": 0
+ },
+ "_seq_no": 0,
+ "_primary_term": 1
}
```
@@ -464,10 +462,10 @@ API 请求方式:GET、POST、PUT、DELETE
```json
{
- "name":"中年xkcoding",
- "age":34,
- "country":"中国",
- "birthday":"1984-11-22"
+ "name": "中年xkcoding",
+ "age": 34,
+ "country": "中国",
+ "birthday": "1984-11-22"
}
```
@@ -475,18 +473,18 @@ API 请求方式:GET、POST、PUT、DELETE
```json
{
- "_index": "people",
- "_type": "person",
- "_id": "I4_sAmEBVnl1iCRF78pG",
- "_version": 1,
- "result": "created",
- "_shards": {
- "total": 2,
- "successful": 2,
- "failed": 0
- },
- "_seq_no": 0,
- "_primary_term": 1
+ "_index": "people",
+ "_type": "person",
+ "_id": "I4_sAmEBVnl1iCRF78pG",
+ "_version": 1,
+ "result": "created",
+ "_shards": {
+ "total": 2,
+ "successful": 2,
+ "failed": 0
+ },
+ "_seq_no": 0,
+ "_primary_term": 1
}
```
@@ -496,26 +494,25 @@ API 请求方式:GET、POST、PUT、DELETE
然后前往 head 插件查看是否插入数据
-

### 4.4. 修改
-修改文档数据,打开 PostMan,输入地址 http://localhost:9200/people/person/1/_update ,将请求的 json 放入 Body 请求体,将方法改为 POST 方法,点击 Send。
+修改文档数据,打开 PostMan,输入地址 http://localhost:9200/people/person/1/\_update ,将请求的 json 放入 Body 请求体,将方法改为 POST 方法,点击 Send。
请求体:
```json
{
- "doc":{
- "name":"沈扬凯"
- }
+ "doc": {
+ "name": "沈扬凯"
+ }
}
```
-{% note danger %}
+{% note danger %}
-注意:修改的时候 URL 必须后面跟_update,然后要修改的字段必须放在 doc 字段里。
+注意:修改的时候 URL 必须后面跟\_update,然后要修改的字段必须放在 doc 字段里。
{% endnote%}
@@ -523,18 +520,18 @@ API 请求方式:GET、POST、PUT、DELETE
```json
{
- "_index": "people",
- "_type": "person",
- "_id": "1",
- "_version": 2,
- "result": "updated",
- "_shards": {
- "total": 2,
- "successful": 2,
- "failed": 0
- },
- "_seq_no": 1,
- "_primary_term": 1
+ "_index": "people",
+ "_type": "person",
+ "_id": "1",
+ "_version": 2,
+ "result": "updated",
+ "_shards": {
+ "total": 2,
+ "successful": 2,
+ "failed": 0
+ },
+ "_seq_no": 1,
+ "_primary_term": 1
}
```
@@ -550,18 +547,18 @@ API 请求方式:GET、POST、PUT、DELETE
```json
{
- "_index": "people",
- "_type": "person",
- "_id": "1",
- "_version": 3,
- "result": "deleted",
- "_shards": {
- "total": 2,
- "successful": 2,
- "failed": 0
- },
- "_seq_no": 2,
- "_primary_term": 1
+ "_index": "people",
+ "_type": "person",
+ "_id": "1",
+ "_version": 3,
+ "result": "deleted",
+ "_shards": {
+ "total": 2,
+ "successful": 2,
+ "failed": 0
+ },
+ "_seq_no": 2,
+ "_primary_term": 1
}
```
@@ -579,17 +576,17 @@ ES 正如其名,本就是为了 Search 而生,所以查询是 ES 最牛逼
```json
{
- "_index": "people",
- "_type": "person",
- "_id": "1",
- "_version": 1,
- "found": true,
- "_source": {
- "name": "xkcoding",
- "age": 24,
- "country": "中国",
- "birthday": "1994-11-22"
- }
+ "_index": "people",
+ "_type": "person",
+ "_id": "1",
+ "_version": 1,
+ "found": true,
+ "_source": {
+ "name": "xkcoding",
+ "age": 24,
+ "country": "中国",
+ "birthday": "1994-11-22"
+ }
}
```
@@ -597,11 +594,11 @@ ES 正如其名,本就是为了 Search 而生,所以查询是 ES 最牛逼
这里还是使用 PostMan 来测试我们的条件查询
-请求地址:http://localhost:9200/people/_search
+请求地址:http://localhost:9200/people/\_search
请求方式:POST
-{% note danger %}
+{% note danger %}
返回值因为文章篇幅有限,就不将返回的内容贴在文章中了,请自行测试下方请求体,验证结果
@@ -613,9 +610,9 @@ ES 正如其名,本就是为了 Search 而生,所以查询是 ES 最牛逼
```json
{
- "query":{
- "match_all":{}
- }
+ "query": {
+ "match_all": {}
+ }
}
```
@@ -625,11 +622,11 @@ ES 正如其名,本就是为了 Search 而生,所以查询是 ES 最牛逼
```json
{
- "query":{
- "match_all":{}
- },
- "from":0,
- "size":3
+ "query": {
+ "match_all": {}
+ },
+ "from": 0,
+ "size": 3
}
```
@@ -639,11 +636,11 @@ ES 正如其名,本就是为了 Search 而生,所以查询是 ES 最牛逼
```json
{
- "query":{
- "match":{
- "name":"小"
- }
- }
+ "query": {
+ "match": {
+ "name": "小"
+ }
+ }
}
```
@@ -653,24 +650,24 @@ ES 正如其名,本就是为了 Search 而生,所以查询是 ES 最牛逼
```json
{
- "query":{
- "match":{
- "name":"小"
- }
- },
- "sort":[
- {
- "birthday":{
- "order":"desc"
- }
- }
- ]
+ "query": {
+ "match": {
+ "name": "小"
+ }
+ },
+ "sort": [
+ {
+ "birthday": {
+ "order": "desc"
+ }
+ }
+ ]
}
```
#### 聚合查询
-请求地址:http://localhost:9200/people/_search
+请求地址:http://localhost:9200/people/\_search
请求方式:POST
@@ -680,13 +677,13 @@ ES 正如其名,本就是为了 Search 而生,所以查询是 ES 最牛逼
```json
{
- "aggs":{
- "group_by_age":{
- "terms":{
- "field":"age"
- }
- }
- }
+ "aggs": {
+ "group_by_age": {
+ "terms": {
+ "field": "age"
+ }
+ }
+ }
}
```
@@ -696,18 +693,18 @@ ES 正如其名,本就是为了 Search 而生,所以查询是 ES 最牛逼
```json
{
- "aggs":{
- "group_by_age":{
- "terms":{
- "field":"age"
- }
- },
- "group_by_birthday":{
- "terms":{
- "field":"birthday"
- }
- }
- }
+ "aggs": {
+ "group_by_age": {
+ "terms": {
+ "field": "age"
+ }
+ },
+ "group_by_birthday": {
+ "terms": {
+ "field": "birthday"
+ }
+ }
+ }
}
```
@@ -717,13 +714,13 @@ ES 正如其名,本就是为了 Search 而生,所以查询是 ES 最牛逼
```json
{
- "aggs":{
- "grades_ages":{
- "min":{
- "field":"age"
- }
- }
- }
+ "aggs": {
+ "grades_ages": {
+ "min": {
+ "field": "age"
+ }
+ }
+ }
}
```
@@ -733,13 +730,13 @@ ES 正如其名,本就是为了 Search 而生,所以查询是 ES 最牛逼
```json
{
- "aggs":{
- "grades_ages":{
- "max":{
- "field":"age"
- }
- }
- }
+ "aggs": {
+ "grades_ages": {
+ "max": {
+ "field": "age"
+ }
+ }
+ }
}
```
@@ -749,13 +746,13 @@ ES 正如其名,本就是为了 Search 而生,所以查询是 ES 最牛逼
```json
{
- "aggs":{
- "grades_ages":{
- "avg":{
- "field":"age"
- }
- }
- }
+ "aggs": {
+ "grades_ages": {
+ "avg": {
+ "field": "age"
+ }
+ }
+ }
}
```
@@ -765,13 +762,13 @@ ES 正如其名,本就是为了 Search 而生,所以查询是 ES 最牛逼
```json
{
- "aggs":{
- "grades_ages":{
- "sum":{
- "field":"age"
- }
- }
- }
+ "aggs": {
+ "grades_ages": {
+ "sum": {
+ "field": "age"
+ }
+ }
+ }
}
```
@@ -781,13 +778,13 @@ ES 正如其名,本就是为了 Search 而生,所以查询是 ES 最牛逼
```json
{
- "aggs":{
- "grades_ages":{
- "stats":{
- "field":"age"
- }
- }
- }
+ "aggs": {
+ "grades_ages": {
+ "stats": {
+ "field": "age"
+ }
+ }
+ }
}
```
@@ -805,11 +802,11 @@ ES 正如其名,本就是为了 Search 而生,所以查询是 ES 最牛逼
```json
{
- "query":{
- "match":{
- "name":"小"
- }
- }
+ "query": {
+ "match": {
+ "name": "小"
+ }
+ }
}
```
@@ -819,11 +816,11 @@ ES 正如其名,本就是为了 Search 而生,所以查询是 ES 最牛逼
```json
{
- "query":{
- "match":{
- "name":"小凯"
- }
- }
+ "query": {
+ "match": {
+ "name": "小凯"
+ }
+ }
}
```
@@ -881,11 +878,11 @@ ES 正如其名,本就是为了 Search 而生,所以查询是 ES 最牛逼
```json
{
- "query":{
- "match_phrase":{
- "name":"小凯"
- }
- }
+ "query": {
+ "match_phrase": {
+ "name": "小凯"
+ }
+ }
}
```
@@ -897,11 +894,11 @@ ES 正如其名,本就是为了 Search 而生,所以查询是 ES 最牛逼
```json
{
- "query":{
- "term":{
- "country":"中国"
- }
- }
+ "query": {
+ "term": {
+ "country": "中国"
+ }
+ }
}
```
@@ -913,14 +910,14 @@ ES 正如其名,本就是为了 Search 而生,所以查询是 ES 最牛逼
```json
{
- "query":{
- "range":{
- "age":{
- "gte":24,
- "lt":30
- }
- }
- }
+ "query": {
+ "range": {
+ "age": {
+ "gte": 24,
+ "lt": 30
+ }
+ }
+ }
}
```
@@ -938,15 +935,15 @@ ES 正如其名,本就是为了 Search 而生,所以查询是 ES 最牛逼
```json
{
- "query":{
- "bool":{
- "filter":{
- "term":{
- "age":23
- }
- }
- }
- }
+ "query": {
+ "bool": {
+ "filter": {
+ "term": {
+ "age": 23
+ }
+ }
+ }
+ }
}
```
@@ -960,15 +957,15 @@ ES 正如其名,本就是为了 Search 而生,所以查询是 ES 最牛逼
```json
{
- "query":{
- "constant_score":{
- "filter":{
- "match":{
- "name":"小"
- }
- }
- }
- }
+ "query": {
+ "constant_score": {
+ "filter": {
+ "match": {
+ "name": "小"
+ }
+ }
+ }
+ }
}
```
@@ -978,16 +975,16 @@ ES 正如其名,本就是为了 Search 而生,所以查询是 ES 最牛逼
```json
{
- "query":{
- "constant_score":{
- "filter":{
- "match":{
- "name":"小"
- }
- },
- "boost":2
- }
- }
+ "query": {
+ "constant_score": {
+ "filter": {
+ "match": {
+ "name": "小"
+ }
+ },
+ "boost": 2
+ }
+ }
}
```
@@ -999,21 +996,22 @@ ES 正如其名,本就是为了 Search 而生,所以查询是 ES 最牛逼
```json
{
- "query":{
- "bool":{
- "should":[
- {
- "match":{
- "country":"中国"
- }
- },{
- "match":{
- "age":30
- }
- }
- ]
- }
- }
+ "query": {
+ "bool": {
+ "should": [
+ {
+ "match": {
+ "country": "中国"
+ }
+ },
+ {
+ "match": {
+ "age": 30
+ }
+ }
+ ]
+ }
+ }
}
```
@@ -1023,21 +1021,22 @@ ES 正如其名,本就是为了 Search 而生,所以查询是 ES 最牛逼
```json
{
- "query":{
- "bool":{
- "must":[
- {
- "match":{
- "country":"中国"
- }
- },{
- "match":{
- "age":23
- }
- }
- ]
- }
- }
+ "query": {
+ "bool": {
+ "must": [
+ {
+ "match": {
+ "country": "中国"
+ }
+ },
+ {
+ "match": {
+ "age": 23
+ }
+ }
+ ]
+ }
+ }
}
```
@@ -1047,28 +1046,31 @@ ES 正如其名,本就是为了 Search 而生,所以查询是 ES 最牛逼
```json
{
- "query":{
- "bool":{
- "must":[
- {
- "match":{
- "country":"中国"
- }
- },{
- "match":{
- "age":23
- }
- }
- ],
- "filter":[{
- "range":{
- "birthday":{
- "lt":"1995-07-01"
- }
- }
- }]
- }
- }
+ "query": {
+ "bool": {
+ "must": [
+ {
+ "match": {
+ "country": "中国"
+ }
+ },
+ {
+ "match": {
+ "age": 23
+ }
+ }
+ ],
+ "filter": [
+ {
+ "range": {
+ "birthday": {
+ "lt": "1995-07-01"
+ }
+ }
+ }
+ ]
+ }
+ }
}
```
@@ -1199,10 +1201,10 @@ public ApiResponse add(@RequestParam String name,
* @return UPDATED 代表文档修改成功
*/
@PutMapping("/person/{id}")
-public ApiResponse update(@PathVariable String id,
- @RequestParam(value = "name", required = false) String name,
- @RequestParam(value = "country", required = false) String country,
- @RequestParam(value = "age", required = false) Integer age,
+public ApiResponse update(@PathVariable String id,
+ @RequestParam(value = "name", required = false) String name,
+ @RequestParam(value = "country", required = false) String country,
+ @RequestParam(value = "age", required = false) Integer age,
@RequestParam(value = "birthday", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date birthday) {
UpdateRequest request = new UpdateRequest(INDEX, TYPE, id);
try {
@@ -1335,4 +1337,4 @@ ElasticSearch 在现在是一个应用十分广泛,且火热的技术,本文
推荐学习地址:
1. 官方文档:https://www.elastic.co/guide/en/elasticsearch/reference/current/getting-started.html
-2. Elastic 中文社区:https://elasticsearch.cn/
\ No newline at end of file
+2. Elastic 中文社区:https://elasticsearch.cn/
diff --git a/source/_posts/2018-01-16.db-page-sqls.md b/src/data/blog/2018-01-16.db-page-sqls.md
similarity index 67%
rename from source/_posts/2018-01-16.db-page-sqls.md
rename to src/data/blog/2018-01-16.db-page-sqls.md
index 1b91efe0..b9f7cac0 100644
--- a/source/_posts/2018-01-16.db-page-sqls.md
+++ b/src/data/blog/2018-01-16.db-page-sqls.md
@@ -1,19 +1,16 @@
---
-title: 关于 MSSQL/MYSQL/ORACLE 的分页语句
-comments: true
-fancybox: false
-toc: false
-date: 2018-01-16 14:07:37
+title: "关于 MSSQL/MYSQL/ORACLE 的分页语句"
+author: xkcoding
+pubDatetime: 2018-01-16T06:07:37.000Z
+slug: db-page-sqls
+featured: false
+draft: false
tags:
-- 代码片段
-- sql
-categories:
-- 代码片段
-- sql
-description: 不同数据库的前 N 条或者第 N 条到第 M 条记录的 SQL 语句
-permalink: db-page-sqls
-top:
+ - 代码片段
+ - sql
+description: "MSSQL 用 TOP,MySQL 用 LIMIT,Oracle 用 ROWNUM——三大数据库的分页语法完全不同。本文整理了各数据库查询前 N 条记录和分页查询的标准写法,直接复制就能用。特别是 Oracle 的三层嵌套结构,不熟悉的人很容易写错。"
---
+
**MSSQL 的前 N 条记录**
```sql
@@ -53,4 +50,3 @@ SELECT * FROM TABLE_NAME WHERE ROWNUM < N
-- 这句就是从 N 到 M 的数据,分为三层结构
SELECT * FROM ( SELECT T.*,T.ROWNUM RN FROM ( SELECT * FROM TABLE_NAME ) T WHERE T.ROWNUM < M ) WHERE RN > N
```
-
diff --git a/source/_posts/2018-02-07.regular-expression-list.md b/src/data/blog/2018-02-07.regular-expression-list.md
similarity index 87%
rename from source/_posts/2018-02-07.regular-expression-list.md
rename to src/data/blog/2018-02-07.regular-expression-list.md
index c10278ba..a944cf42 100644
--- a/source/_posts/2018-02-07.regular-expression-list.md
+++ b/src/data/blog/2018-02-07.regular-expression-list.md
@@ -1,19 +1,15 @@
---
-title: 常用正则表达式
-comments: true
-fancybox: false
-toc: false
-date: 2018-02-07 11:45:02
+title: "常用正则表达式"
+author: xkcoding
+pubDatetime: 2018-02-07T03:45:02.000Z
+slug: regular-expression-list
+featured: false
+draft: false
tags:
-- 代码片段
-- regex
-categories:
-- 代码片段
-- regex
-description: 最全的常用正则表达式大全——包括校验数字、字符、一些特殊的需求等等
-permalink: regular-expression-list
-top:
+ - 代码片段
+description: "手机号、身份证、邮箱、IP 地址、金额格式……这些常用正则表达式你每次都要现查?本文收集了开发中最常用的正则表达式:数字校验、字符校验、特殊格式校验,直接复制粘贴就能用。还有强密码规则、中文字符匹配、首尾空白去除等实用表达式。"
---
+
## 校验数字的表达式
```js
@@ -62,24 +58,24 @@ Email地址:^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$
域名:[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(/.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+/.?
InternetURL:[a-zA-z]+://[^\s]* 或 ^http://([\w-]+\.)+[\w-]+(/[\w-./?%&=]*)?$
手机号码:^(13[0-9]|14[0-9]|15[0-9]|166|17[0-9]|18[0-9]|19[8|9])\d{8}$
-电话号码("XXX-XXXXXXX"、"XXXX-XXXXXXXX"、"XXX-XXXXXXX"、"XXX-XXXXXXXX"、"XXXXXXX"和"XXXXXXXX):^(\(\d{3,4}-)|\d{3.4}-)?\d{7,8}$
-国内电话号码(0511-4405222、021-87888822):\d{3}-\d{8}|\d{4}-\d{7}
+电话号码("XXX-XXXXXXX"、"XXXX-XXXXXXXX"、"XXX-XXXXXXX"、"XXX-XXXXXXXX"、"XXXXXXX"和"XXXXXXXX):^(\(\d{3,4}-)|\d{3.4}-)?\d{7,8}$
+国内电话号码(0511-4405222、021-87888822):\d{3}-\d{8}|\d{4}-\d{7}
18位身份证号码(数字、字母x结尾):^((\d{18})|([0-9x]{18})|([0-9X]{18}))$
帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线):^[a-zA-Z][a-zA-Z0-9_]{4,15}$
密码(以字母开头,长度在6~18之间,只能包含字母、数字和下划线):^[a-zA-Z]\w{5,17}$
-强密码(必须包含大小写字母和数字的组合,不能使用特殊字符,长度在8-10之间):^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,10}$
+强密码(必须包含大小写字母和数字的组合,不能使用特殊字符,长度在8-10之间):^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,10}$
日期格式:^\d{4}-\d{1,2}-\d{1,2}
一年的12个月(01~09和1~12):^(0?[1-9]|1[0-2])$
-一个月的31天(01~09和1~31):^((0?[1-9])|((1|2)[0-9])|30|31)$
+一个月的31天(01~09和1~31):^((0?[1-9])|((1|2)[0-9])|30|31)$
钱的输入格式:
- 1.有四种钱的表示形式我们可以接受:"10000.00" 和 "10,000.00", 和没有 "分" 的 "10000" 和 "10,000":^[1-9][0-9]*$
- 2.这表示任意一个不以0开头的数字,但是,这也意味着一个字符"0"不通过,所以我们采用下面的形式:^(0|[1-9][0-9]*)$
- 3.一个0或者一个不以0开头的数字.我们还可以允许开头有一个负号:^(0|-?[1-9][0-9]*)$
- 4.这表示一个0或者一个可能为负的开头不为0的数字.让用户以0开头好了.把负号的也去掉,因为钱总不能是负的吧.下面我们要加的是说明可能的小数部分:^[0-9]+(.[0-9]+)?$
- 5.必须说明的是,小数点后面至少应该有1位数,所以"10."是不通过的,但是 "10" 和 "10.2" 是通过的:^[0-9]+(.[0-9]{2})?$
- 6.这样我们规定小数点后面必须有两位,如果你认为太苛刻了,可以这样:^[0-9]+(.[0-9]{1,2})?$
- 7.这样就允许用户只写一位小数.下面我们该考虑数字中的逗号了,我们可以这样:^[0-9]{1,3}(,[0-9]{3})*(.[0-9]{1,2})?$
- 8.1到3个数字,后面跟着任意个 逗号+3个数字,逗号成为可选,而不是必须:^([0-9]+|[0-9]{1,3}(,[0-9]{3})*)(.[0-9]{1,2})?$
+ 1.有四种钱的表示形式我们可以接受:"10000.00" 和 "10,000.00", 和没有 "分" 的 "10000" 和 "10,000":^[1-9][0-9]*$
+ 2.这表示任意一个不以0开头的数字,但是,这也意味着一个字符"0"不通过,所以我们采用下面的形式:^(0|[1-9][0-9]*)$
+ 3.一个0或者一个不以0开头的数字.我们还可以允许开头有一个负号:^(0|-?[1-9][0-9]*)$
+ 4.这表示一个0或者一个可能为负的开头不为0的数字.让用户以0开头好了.把负号的也去掉,因为钱总不能是负的吧.下面我们要加的是说明可能的小数部分:^[0-9]+(.[0-9]+)?$
+ 5.必须说明的是,小数点后面至少应该有1位数,所以"10."是不通过的,但是 "10" 和 "10.2" 是通过的:^[0-9]+(.[0-9]{2})?$
+ 6.这样我们规定小数点后面必须有两位,如果你认为太苛刻了,可以这样:^[0-9]+(.[0-9]{1,2})?$
+ 7.这样就允许用户只写一位小数.下面我们该考虑数字中的逗号了,我们可以这样:^[0-9]{1,3}(,[0-9]{3})*(.[0-9]{1,2})?$
+ 8.1到3个数字,后面跟着任意个 逗号+3个数字,逗号成为可选,而不是必须:^([0-9]+|[0-9]{1,3}(,[0-9]{3})*)(.[0-9]{1,2})?$
备注:这就是最终结果了,别忘了"+"可以用"*"替代如果你觉得空字符串也可以接受的话(奇怪,为什么?)最后,别忘了在用函数时去掉去掉那个反斜杠,一般的错误都在这里
xml文件:^([a-zA-Z]+-?)+[a-zA-Z0-9]+\\.[x|X][m|M][l|L]$
中文字符的正则表达式:[\u4e00-\u9fa5]
@@ -92,4 +88,3 @@ HTML标记的正则表达式:<(\S*?)[^>]*>.*?\1>|<.*? /> (网上流传的
IP地址:\d+\.\d+\.\d+\.\d+ (提取IP地址时有用)
IP地址:((?:(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d)\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d))
```
-
diff --git a/source/_posts/2018-07-30.java8-learning.md b/src/data/blog/2018-07-30.java8-learning.md
similarity index 88%
rename from source/_posts/2018-07-30.java8-learning.md
rename to src/data/blog/2018-07-30.java8-learning.md
index ac789c65..0e2924a3 100644
--- a/source/_posts/2018-07-30.java8-learning.md
+++ b/src/data/blog/2018-07-30.java8-learning.md
@@ -1,18 +1,15 @@
---
-title: Java8 学习整理
-comments: true
-fancybox: false
-toc: true
-date: 2018-07-30 10:39:07
+title: "Java8 学习整理"
+author: xkcoding
+pubDatetime: 2018-07-30T02:39:07.000Z
+slug: java8-learning
+featured: false
+draft: false
tags:
-- java
-categories:
-- 技术相关
-- java
-description:
-permalink: java8-learning
-top:
+ - java
+description: "公司内部 Java 8 分享的完整文档!从 JDK 5 到 JDK 8 的发展历程梳理,重点讲解 Lambda 表达式、函数式接口、Stream API、Optional 类、新日期时间 API 等核心特性。每个特性都附带 Demo 代码,拿来即用。还在用 for 循环?是时候拥抱函数式编程了。"
---
+
前言:Java 8 (又称为 jdk 1.8) 是 Java 语言开发的一个主要版本。 Oracle 公司于 2014 年 3 月 18 日发布 Java 8 ,它支持函数式编程,新的 JavaScript 引擎,新的日期 API,新的Stream API 等。目前大部分公司都已经在使用 Java 8开发项目。本文是我在公司的一次Java 8分享的文章,开篇先介绍了 **Java 8 的发展**,再分别讲述 **Java 8 的新特性**,同时附上一些 Demo 代码( https://github.com/xkcoding/java8_learning ),以便更好的入门学习 Java 8,厚脸皮的求个 Star。
@@ -68,7 +65,7 @@ public enum MyEnum{
例如:
```java
-public void printColor(String red,String green,String yellow){
+public void printColor(String red,String green,String yellow){
}
```
@@ -114,8 +111,8 @@ List strs = new ArrayList();
例如上面这个集合我们可以通过for-each遍历,这样更加简单清晰。
```java
-for(String s : strs){
- System.out.println(s);
+for(String s : strs){
+ System.out.println(s);
}
```
@@ -172,31 +169,31 @@ JDK1.7可以在数值类型的变量里添加下滑线。
例如:
```java
-int num = 1234_5678_9;
-float num2 = 222_33F;
+int num = 1234_5678_9;
+float num2 = 222_33F;
long num3 = 123_000_111L;
```
注意,有几个地方是不能添加的:
-1. 数字的开头和结尾
-2. 小数点前后
+1. 数字的开头和结尾
+2. 小数点前后
3. F或者L前
**switch对String的支持**
```java
-String status = "orderState";
-switch (status) {
- case "ordercancel":
- System.out.println("订单取消");
- break;
- case "orderSuccess":
- System.out.println("预订成功");
- break;
- default:
- System.out.println("状态未知");
-}
+String status = "orderState";
+switch (status) {
+ case "ordercancel":
+ System.out.println("订单取消");
+ break;
+ case "orderSuccess":
+ System.out.println("预订成功");
+ break;
+ default:
+ System.out.println("状态未知");
+}
```
**try-with-resource**
@@ -209,31 +206,31 @@ switch (status) {
例如:
```java
-public static String readFirstLineFromFile(String path) throws IOException {
+public static String readFirstLineFromFile(String path) throws IOException {
- try (BufferedReader br = new BufferedReader(new FileReader(path))) {
- return br.readLine();
- }
-}
+ try (BufferedReader br = new BufferedReader(new FileReader(path))) {
+ return br.readLine();
+ }
+}
```
-在 java 7 以及以后的版本里,`BufferedReader` 实现了 `java.lang.AutoCloseable` 接口。
+在 java 7 以及以后的版本里,`BufferedReader` 实现了 `java.lang.AutoCloseable` 接口。
由于 `BufferedReader` 定义在 `try-with-resources` 声明里,无论 `try` 语句正常还是异常的结束,
它都会自动的关掉。而在 java7 以前,你需要使用 `finally` 块来关掉这个对象。
**捕获多种异常并用改进后的类型检查来重新抛出异常**
```java
-public static void first(){
- try {
- BufferedReader reader = new BufferedReader(new FileReader(""));
- Connection con = null;
- Statement stmt = con.createStatement();
- } catch (IOException | SQLException e) {
- //捕获多个异常,e就是final类型的
- e.printStackTrace();
- }
-}
+public static void first(){
+ try {
+ BufferedReader reader = new BufferedReader(new FileReader(""));
+ Connection con = null;
+ Statement stmt = con.createStatement();
+ } catch (IOException | SQLException e) {
+ //捕获多个异常,e就是final类型的
+ e.printStackTrace();
+ }
+}
```
优点:用一个 `catch` 处理多个异常,比用多个 `catch` 每个处理一个异常生成的字节码要更小更高效。
@@ -254,16 +251,16 @@ List list = new ArrayList<>();
```
因为编译器可以从前面(List)推断出推断出类型参数,所以后面的 `ArrayList` 之后可以不用写泛型参数了,只用一对空着的尖括号就行。
-当然,你必须带着菱形 `<>`,否则会有警告的。
+当然,你必须带着菱形 `<>`,否则会有警告的。
Java SE7 只支持有限的类型推断:只有构造器的参数化类型在上下文中被显著的声明了,你才可以使用类型推断,否则不行。
```java
List list = new ArrayList<>();
-list.add("A");
-//这个不行
-list.addAll(new ArrayList<>());
-// 这个可以
-List extends String> list2 = new ArrayList<>();
+list.add("A");
+//这个不行
+list.addAll(new ArrayList<>());
+// 这个可以
+List extends String> list2 = new ArrayList<>();
list.addAll(list2);
```
@@ -359,7 +356,7 @@ PS:个人理解类似前端 ES6 的箭头函数
### 2.2. 什么是函数式编程?
-每个人对函数式编程的理解不尽相同。 我的理解是:**在完成一个编程任务时,通过使用不可变的值或函数,对他们进行处理,然后得到另一个值的过程。**
+每个人对函数式编程的理解不尽相同。 我的理解是:**在完成一个编程任务时,通过使用不可变的值或函数,对他们进行处理,然后得到另一个值的过程。**
不同的语言社区往往对各自语言中的特性孤芳自赏。现在谈 Java 程序员如何定义函数式编程还为时尚早,但是,这根本不重要!
我们关心的是如何写出好代码,而不是符合函数式编程风格的代码。
@@ -386,21 +383,21 @@ PS:个人理解类似前端 ES6 的箭头函数
### 2.7. Lambda 及函数式接口的例子
-| 使用案例 | Lambda 的例子 | 对应的函数式接口 |
-| :-------------------: | :----------------------------------------------------------- | :----------------------------------------------------------- |
-| 布尔表达式 | `(List list) -> list.isEmpty()` | `Predicate>` |
-| 创建对象 | `() -> new Project()` | `Supplier` |
-| 消费一个对象 | `(Project p) -> System.out.println(p.getStars())` | `Consumer` |
-| 从一个对象中选择/提取 | `(int a, int b) -> a * b` | `IntBinaryOperator` |
+| 使用案例 | Lambda 的例子 | 对应的函数式接口 |
+| :-------------------: | :------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------- |
+| 布尔表达式 | `(List list) -> list.isEmpty()` | `Predicate>` |
+| 创建对象 | `() -> new Project()` | `Supplier` |
+| 消费一个对象 | `(Project p) -> System.out.println(p.getStars())` | `Consumer` |
+| 从一个对象中选择/提取 | `(int a, int b) -> a * b` | `IntBinaryOperator` |
| 比较两个对象 | `(Project p1, Project p2) -> p1.getStars().compareTo(p2.getStars())` | `Comparator 或 BiFunction `Project, Integer> 或 ToIntBiFunction` |
### 2.8. Lambda 小结
- lambda 表达式可以理解为一种匿名函数:它没有名称,但有参数列表、函数主体、返回 类型,可能还有一个可以抛出的异常的列表。
- lambda 表达式让你可以简洁地传递代码。
-- 只有在接受函数式接口的地方才可以使用 lambda 表达式。
-- lambda 表达式允许你直接内联,为函数式接口的抽象方法提供实现,并且将整个表达式作为函数式接口的一个实例。
-- Lambda表达式所需要代表的类型称为目标类型。
+- 只有在接受函数式接口的地方才可以使用 lambda 表达式。
+- lambda 表达式允许你直接内联,为函数式接口的抽象方法提供实现,并且将整个表达式作为函数式接口的一个实例。
+- Lambda表达式所需要代表的类型称为目标类型。
## 3. 方法引用
@@ -408,7 +405,7 @@ PS:个人理解类似前端 ES6 的箭头函数
方法引用可以使语言的构造更紧凑简洁,减少冗余代码。
-方法引用使用一对冒号` :: `。
+方法引用使用一对冒号`::`。
示例:
@@ -451,17 +448,17 @@ JDK 1.8 新增加的函数接口:
**Java 8中的常用函数式接口**
-| 函数式接口 | 函数描述符 | 原始类型特化 |
-| :-----------------: | :--------------- | :----------------------------------------------------------- |
-| `Predicate` | `T->boolean` | `IntPredicate,LongPredicate, DoublePredicate` |
-| `Consumer` | `T->void` | `IntConsumer,LongConsumer, DoubleConsumer` |
+| 函数式接口 | 函数描述符 | 原始类型特化 |
+| :-----------------: | :--------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `Predicate` | `T->boolean` | `IntPredicate,LongPredicate, DoublePredicate` |
+| `Consumer` | `T->void` | `IntConsumer,LongConsumer, DoubleConsumer` |
| `Function` | `T->R` | `IntFunction, IntToDoubleFunction,` `IntToLongFunction, LongFunction,` `LongToDoubleFunction, LongToIntFunction, ` `DoubleFunction, ToIntFunction, ` `ToDoubleFunction, ToLongFunction` |
-| `Supplier` | `()->T` | `BooleanSupplier,IntSupplier, LongSupplier, DoubleSupplier` |
-| `UnaryOperator` | `T->T` | `IntUnaryOperator, LongUnaryOperator, DoubleUnaryOperator` |
-| `BinaryOperator` | `(T,T)->T` | `IntBinaryOperator, LongBinaryOperator, DoubleBinaryOperator` |
-| `BiPredicate` | `(L,R)->boolean` | |
-| `BiConsumer` | `(T,U)->void` | `ObjIntConsumer, ObjLongConsumer, ObjDoubleConsumer` |
-| `BiFunction` | `(T,U)->R` | `ToIntBiFunction, ToLongBiFunction, ToDoubleBiFunction` |
+| `Supplier` | `()->T` | `BooleanSupplier,IntSupplier, LongSupplier, DoubleSupplier` |
+| `UnaryOperator` | `T->T` | `IntUnaryOperator, LongUnaryOperator, DoubleUnaryOperator` |
+| `BinaryOperator` | `(T,T)->T` | `IntBinaryOperator, LongBinaryOperator, DoubleBinaryOperator` |
+| `BiPredicate` | `(L,R)->boolean` | |
+| `BiConsumer` | `(T,U)->void` | `ObjIntConsumer, ObjLongConsumer, ObjDoubleConsumer` |
+| `BiFunction` | `(T,U)->R` | `ToIntBiFunction, ToLongBiFunction, ToDoubleBiFunction` |
## 5. 默认方法
@@ -524,7 +521,7 @@ JDK 1.8 新增加的函数接口:
这种数据源较为常用,通过stream()方法即可获取流对象:
```java
-List list = new ArrayList();
+List list = new ArrayList();
Stream stream = list.stream();
```
@@ -835,22 +832,22 @@ Collectors类专门为汇总提供了一个工厂方法:`Collectors.summingInt
**Collectors 类的静态工厂方法**
-| 工厂方法 | 返回类型 | 用途 | 示例 |
-| :-----------------: | :--------------------- | :----------------------------------------------------------- | :----------------------------------------------------------- |
-| `toList` | `List` | 把流中所有项目收集到一个 List | `List projects = projectStream.collect(toList());` |
-| `toSet` | `Set` | 把流中所有项目收集到一个 Set,删除重复项 | `Set projects = projectStream.collect(toSet());` |
-| `toCollection` | `Collection` | 把流中所有项目收集到给定的供应源创建的集合 | `Collection projects = projectStream.collect(toCollection(), ArrayList::new);` |
-| `counting` | `Long` | 计算流中元素的个数 | `long howManyProjects = projectStream.collect(counting());` |
-| `summingInt` | `Integer` | 对流中项目的一个整数属性求和 | `int totalStars = projectStream.collect(summingInt(Project::getStars));` |
-| `averagingInt` | `Double` | 计算流中项目 Integer 属性的平均值 | `double avgStars = projectStream.collect(averagingInt(Project::getStars));` |
-| `summarizingInt` | `IntSummaryStatistics` | 收集关于流中项目 Integer 属性的统计值,例如最大、最小、 总和与平均值 | `IntSummaryStatistics projectStatistics = projectStream.collect(summarizingInt(Project::getStars));` |
-| `joining` | `String` | 连接对流中每个项目调用 toString 方法所生成的字符串 | `String shortProject = projectStream.map(Project::getName).collect(joining(", "));` |
-| `maxBy` | `Optional` | 按照给定比较器选出的最大元素的 Optional, 或如果流为空则为 Optional.empty() | `Optional fattest = projectStream.collect(maxBy(comparingInt(Project::getStars)));` |
-| `minBy` | `Optional` | 按照给定比较器选出的最小元素的 Optional, 或如果流为空则为 Optional.empty() | `Optional fattest = projectStream.collect(minBy(comparingInt(Project::getStars)));` |
-| `reducing` | 归约操作产生的类型 | 从一个作为累加器的初始值开始,利用 BinaryOperator 与流中的元素逐个结合,从而将流归约为单个值 | `int totalStars = projectStream.collect(reducing(0, Project::getStars, Integer::sum));` |
-| `collectingAndThen` | 转换函数返回的类型 | 包含另一个收集器,对其结果应用转换函数 | `int howManyProjects = projectStream.collect(collectingAndThen(toList(), List::size));` |
-| `groupingBy` | `Map>` | 根据项目的一个属性的值对流中的项目作问组,并将属性值作 为结果 Map 的键 | `Map> projectByLanguage = projectStream.collect(groupingBy(Project::getLanguage));` |
-| `partitioningBy` | `Map>` | 根据对流中每个项目应用断言的结果来对项目进行分区 | `Map> vegetarianDishes = projectStream.collect(partitioningBy(Project::isVegetarian));` |
+| 工厂方法 | 返回类型 | 用途 | 示例 |
+| :-----------------: | :--------------------- | :------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------ |
+| `toList` | `List` | 把流中所有项目收集到一个 List | `List projects = projectStream.collect(toList());` |
+| `toSet` | `Set` | 把流中所有项目收集到一个 Set,删除重复项 | `Set projects = projectStream.collect(toSet());` |
+| `toCollection` | `Collection` | 把流中所有项目收集到给定的供应源创建的集合 | `Collection projects = projectStream.collect(toCollection(), ArrayList::new);` |
+| `counting` | `Long` | 计算流中元素的个数 | `long howManyProjects = projectStream.collect(counting());` |
+| `summingInt` | `Integer` | 对流中项目的一个整数属性求和 | `int totalStars = projectStream.collect(summingInt(Project::getStars));` |
+| `averagingInt` | `Double` | 计算流中项目 Integer 属性的平均值 | `double avgStars = projectStream.collect(averagingInt(Project::getStars));` |
+| `summarizingInt` | `IntSummaryStatistics` | 收集关于流中项目 Integer 属性的统计值,例如最大、最小、 总和与平均值 | `IntSummaryStatistics projectStatistics = projectStream.collect(summarizingInt(Project::getStars));` |
+| `joining` | `String` | 连接对流中每个项目调用 toString 方法所生成的字符串 | `String shortProject = projectStream.map(Project::getName).collect(joining(", "));` |
+| `maxBy` | `Optional` | 按照给定比较器选出的最大元素的 Optional, 或如果流为空则为 Optional.empty() | `Optional fattest = projectStream.collect(maxBy(comparingInt(Project::getStars)));` |
+| `minBy` | `Optional` | 按照给定比较器选出的最小元素的 Optional, 或如果流为空则为 Optional.empty() | `Optional fattest = projectStream.collect(minBy(comparingInt(Project::getStars)));` |
+| `reducing` | 归约操作产生的类型 | 从一个作为累加器的初始值开始,利用 BinaryOperator 与流中的元素逐个结合,从而将流归约为单个值 | `int totalStars = projectStream.collect(reducing(0, Project::getStars, Integer::sum));` |
+| `collectingAndThen` | 转换函数返回的类型 | 包含另一个收集器,对其结果应用转换函数 | `int howManyProjects = projectStream.collect(collectingAndThen(toList(), List::size));` |
+| `groupingBy` | `Map>` | 根据项目的一个属性的值对流中的项目作问组,并将属性值作 为结果 Map 的键 | `Map> projectByLanguage = projectStream.collect(groupingBy(Project::getLanguage));` |
+| `partitioningBy` | `Map>` | 根据对流中每个项目应用断言的结果来对项目进行分区 | `Map> vegetarianDishes = projectStream.collect(partitioningBy(Project::isVegetarian));` |
#### 6.5.3. 转换类型
@@ -940,20 +937,20 @@ Optional 类的引入很好的解决空指针异常。
### Optional类的方法
-| 方法 | 描述 |
-| :-----------: | :----------------------------------------------------------- |
-| `empty` | 返回一个空的 Optional 实例 |
-| `filter` | 如果值存在并且满足提供的断言, 就返回包含该值的 Optional 对象;否则返回一个空的 Optional 对象 |
-| `map` | 如果值存在,就对该值执行提供的 mapping 函数调用 |
+| 方法 | 描述 |
+| :-----------: | :------------------------------------------------------------------------------------------------------------- |
+| `empty` | 返回一个空的 Optional 实例 |
+| `filter` | 如果值存在并且满足提供的断言, 就返回包含该值的 Optional 对象;否则返回一个空的 Optional 对象 |
+| `map` | 如果值存在,就对该值执行提供的 mapping 函数调用 |
| `flatMap` | 如果值存在,就对该值执行提供的 mapping 函数调用,返回一个 Optional 类型的值,否则就返 回一个空的 Optional 对象 |
-| `get` | 如果该值存在,将该值用 Optional 封装返回,否则抛出一个 NoSuchElementException 异常 |
-| `ifPresent` | 如果值存在,就执行使用该值的方法调用,否则什么也不做 |
-| `isPresent` | 如果值存在就返回 true,否则返回 false |
-| `of` | 将指定值用 Optional 封装之后返回,如果该值为 null,则抛出一个 NullPointerException 异常 |
-| `ofNullable` | 将指定值用 Optional 封装之后返回,如果该值为 null,则返回一个空的 Optional 对象 |
-| `orElse` | 如果有值则将其返回,否则返回一个默认值 |
-| `orElseGet` | 如果有值则将其返回,否则返回一个由指定的 Supplier 接口生成的值 |
-| `orElseThrow` | 如果有值则将其返回,否则抛出一个由指定的 Supplier 接口生成的异常 |
+| `get` | 如果该值存在,将该值用 Optional 封装返回,否则抛出一个 NoSuchElementException 异常 |
+| `ifPresent` | 如果值存在,就执行使用该值的方法调用,否则什么也不做 |
+| `isPresent` | 如果值存在就返回 true,否则返回 false |
+| `of` | 将指定值用 Optional 封装之后返回,如果该值为 null,则抛出一个 NullPointerException 异常 |
+| `ofNullable` | 将指定值用 Optional 封装之后返回,如果该值为 null,则返回一个空的 Optional 对象 |
+| `orElse` | 如果有值则将其返回,否则返回一个默认值 |
+| `orElseGet` | 如果有值则将其返回,否则返回一个由指定的 Supplier 接口生成的值 |
+| `orElseThrow` | 如果有值则将其返回,否则抛出一个由指定的 Supplier 接口生成的异常 |
## 8. Nashorn, JavaScript 引擎
@@ -1058,10 +1055,10 @@ Clock类提供了访问当前日期和时间的方法,Clock是时区敏感的
某一个特定的时间点也可以使用Instant类来表示,Instant 类也可以用来创建老的 `java.util.Date` 对象。
```java
-Clock clock = Clock.systemDefaultZone();
-long millis = clock.millis();
-Instant instant = clock.instant();
-Date legacyDate = Date.from(instant); // legacy java.util.Date
+Clock clock = Clock.systemDefaultZone();
+long millis = clock.millis();
+Instant instant = clock.instant();
+Date legacyDate = Date.from(instant); // legacy java.util.Date
```
### LocalDate
@@ -1123,7 +1120,7 @@ LocalDate d2 = localDate.minusYears(3);
```
1. 第一行创建出一个新的LocalDate对象d,表示2018.7.19。
-2. 第二行创建了值等于d日期3年后的LocalDate对象,第三行也是一样,只是值改为d日期的三年前。
+2. 第二行创建了值等于d日期3年后的LocalDate对象,第三行也是一样,只是值改为d日期的三年前。
### LocalTime
@@ -1537,7 +1534,7 @@ date.with(temporal -> {
if (dayOfWeek == DayOfWeek.FRIDAY) {
dayToAdd = 3;
}
-
+
// 如果是星期六,增加两天
if (dayOfWeek == DayOfWeek.SATURDAY) {
dayToAdd = 2;
@@ -1589,7 +1586,7 @@ Java 8 内置了 Base64 编码的编码器和解码器,这样不需要使用
Base64工具类提供了一套静态方法获取下面三种BASE64编解码器:
- **基本:**输出被映射到一组字符A-Za-z0-9+/,编码不添加任何行标,输出的解码仅支持A-Za-z0-9+/。
-- **URL:**输出映射到一组字符A-Za-z0-9+_,输出是URL和文件。
+- **URL:**输出映射到一组字符A-Za-z0-9+\_,输出是URL和文件。
- **MIME:**输出隐射到MIME友好格式。输出每行不超过76字符,并且使用'\r'并跟随'\n'作为分割。编码输出最后没有行分割。
示例:
@@ -1620,4 +1617,4 @@ System.out.println(decoded);
示例代码地址:https://github.com/xkcoding/java8_learning
-如果可以的话,麻烦给个 Star 吧 (♥◠‿◠)ノ゙
\ No newline at end of file
+如果可以的话,麻烦给个 Star 吧 (♥◠‿◠)ノ゙
diff --git a/source/_posts/2018-08-20.spring-boot-global-exception-handler.md b/src/data/blog/2018-08-20.spring-boot-global-exception-handler.md
similarity index 93%
rename from source/_posts/2018-08-20.spring-boot-global-exception-handler.md
rename to src/data/blog/2018-08-20.spring-boot-global-exception-handler.md
index 8973bd03..3e596332 100644
--- a/source/_posts/2018-08-20.spring-boot-global-exception-handler.md
+++ b/src/data/blog/2018-08-20.spring-boot-global-exception-handler.md
@@ -1,18 +1,15 @@
---
-title: Spring Boot 全局异常处理
-comments: true
-fancybox: false
-toc: true
-date: 2018-08-20 09:39:36
-tags:
-- spring boot
-categories:
-- 技术相关
-- spring boot
-description:
-permalink: spring-boot-global-exception-handler
-top:
+title: "Spring Boot 全局异常处理"
+author: xkcoding
+pubDatetime: 2018-08-20T01:39:36.000Z
+slug: spring-boot-global-exception-handler
+featured: false
+draft: false
+tags:
+ - spring-boot
+description: "前后端分离时代,接口返回格式必须统一!本文提供完整的 Spring Boot 全局异常处理方案:统一返回格式封装(Api.java)、状态码枚举(Status.java)、自定义业务异常类、404 异常配置、@ControllerAdvice 全局异常处理器。一套代码解决所有异常处理问题,直接复制到项目里用。"
---
+
我们在做 `Web` 应用的时候,请求处理过程中发生错误异常是一个非常常见的情况,但是异常的处理方式和放回的异常内容并没有做一个统一的处理。在现在这种前后端分离的时代,不论响应成功还是失败的数据格式都需要保持一致。
@@ -428,4 +425,4 @@ public class GlobalExceptionHandler {
异常处理的时候,务必在全局异常处理类中,**打印异常日志**,**打印异常日志**,**打印异常日志**!
-相关代码地址:https://github.com/xkcoding/scaffold
\ No newline at end of file
+相关代码地址:https://github.com/xkcoding/scaffold
diff --git a/src/data/blog/2018-09-18.how-to-update-the-fork-project.md b/src/data/blog/2018-09-18.how-to-update-the-fork-project.md
new file mode 100644
index 00000000..6fdb346b
--- /dev/null
+++ b/src/data/blog/2018-09-18.how-to-update-the-fork-project.md
@@ -0,0 +1,78 @@
+---
+title: "git 如何更新 fork 的项目到原项目的最新版本"
+author: xkcoding
+pubDatetime: 2018-09-18T01:04:23.000Z
+slug: how-to-update-the-fork-project
+featured: false
+draft: false
+tags:
+ - git
+ - github
+description: "Fork 的项目落后原仓库几百个 commit 怎么办?本文用 hutool 项目演示完整的同步流程:添加 upstream 远程地址、fetch 原项目更新、merge 到本地分支、push 到自己的仓库。7 步命令搞定,再也不用删掉重新 Fork 了。开源贡献者必备技能。"
+---
+
+在 github 上 fork 了一个项目之后,如何使自己 fork 的项目和原先作者的项目分支保持同步呢,下面我使用 hutool 的项目做示范,手摸手示范如何使 fork 的项目与原项目分支保持同步。
+
+
+
+1. **查看远程的版本库地址**
+
+ ```bash
+ $ git remote -v
+ origin https://github.com/xkcoding/hutool.git (fetch)
+ origin https://github.com/xkcoding/hutool.git (push)
+ ```
+
+2. **添加原项目 git 地址到本地版本库**
+
+ ```bash
+ $ git remote add upstream https://github.com/looly/hutool.git
+ ```
+
+3. **检查版本库是否添加成功**
+
+ ```bash
+ $ git remote -v
+ origin https://github.com/xkcoding/hutool.git (fetch)
+ origin https://github.com/xkcoding/hutool.git (push)
+ upstream https://github.com/looly/hutool.git (fetch)
+ upstream https://github.com/looly/hutool.git (push)
+ ```
+
+4. **原项目更新内容同步到本地**
+
+ ```bash
+ $ git fetch upstream
+ remote: Counting objects: 360, done.
+ remote: Compressing objects: 100% (148/148), done.
+ remote: Total 360 (delta 158), reused 339 (delta 137), pack-reused 0
+ Receiving objects: 100% (360/360), 87.95 KiB | 45.00 KiB/s, done.
+ Resolving deltas: 100% (158/158), completed with 66 local objects.
+ From https://github.com/looly/hutool
+ * [new branch] v4-dev -> upstream/v4-dev
+ * [new branch] v4-master -> upstream/v4-master
+ ```
+
+5. **查看本地分支**
+
+ ```bash
+ $ git branch -a
+ * v4-master
+ remotes/origin/HEAD -> origin/v4-master
+ remotes/origin/v4-dev
+ remotes/origin/v4-master
+ remotes/upstream/v4-dev
+ remotes/upstream/v4-master
+ ```
+
+6. **同步更新内容到本地对应分支**
+
+ ```bash
+ $ git merge upstream/v4-master
+ ```
+
+7. **提交更新内容到 fork 地址**
+
+ ```bash
+ git push
+ ```
diff --git a/source/_posts/2018-09-20.query-language-differences-between-neo4j-and-mysql.md b/src/data/blog/2018-09-20.query-language-differences-between-neo4j-and-mysql.md
similarity index 82%
rename from source/_posts/2018-09-20.query-language-differences-between-neo4j-and-mysql.md
rename to src/data/blog/2018-09-20.query-language-differences-between-neo4j-and-mysql.md
index c74187ca..624cf4d7 100644
--- a/source/_posts/2018-09-20.query-language-differences-between-neo4j-and-mysql.md
+++ b/src/data/blog/2018-09-20.query-language-differences-between-neo4j-and-mysql.md
@@ -1,18 +1,15 @@
---
-title: Neo4j 和 Mysql 查询基本语法的区别
-comments: true
-fancybox: false
-toc: false
-date: 2018-09-20 13:54:31
+title: "Neo4j 和 Mysql 查询基本语法的区别"
+author: xkcoding
+pubDatetime: 2018-09-20T05:54:31.000Z
+slug: query-language-differences-between-neo4j-and-mysql
+featured: false
+draft: false
tags:
-- neo4j
-categories:
-- 技术相关
-- neo4j
-description:
-permalink: query-language-differences-between-neo4j-and-mysql
-top:
+ - neo4j
+description: "图数据库 Neo4j 的 Cypher 查询语言有多简洁?本文用 7 个典型场景对比 MySQL 和 Neo4j 的查询语法:全表扫描、排序分页、条件筛选、模糊查询、多表联查、分组聚合。特别是多表 JOIN,MySQL 要写一堆 JOIN ON,Neo4j 一行箭头搞定。"
---
+
每种数据库都有自己的一套查询语言或者标准,即使是同为关系型数据库的Oracle、Sql Server 和 MySQL,其在一些语法细节上也有差异,更别说 Mongo, Redis 类似的非关系型数据库了。Neo4j 也一样,有自己专属的查询语言 Cypher。深入学习其实发现 Neo4j 的查询语法并不难去学习,因为 Cypher 的语意确实简洁,直观。
@@ -103,4 +100,3 @@ MATCH (:Order)<-[:SOLD]-(e:Employee)
RETURN e.name, count(*) AS cnt
ORDER BY cnt DESC LIMIT 10
```
-
diff --git a/source/_posts/2018-09-28.run-node-service-in-background.md b/src/data/blog/2018-09-28.run-node-service-in-background.md
similarity index 82%
rename from source/_posts/2018-09-28.run-node-service-in-background.md
rename to src/data/blog/2018-09-28.run-node-service-in-background.md
index c51439ed..1bcffbd4 100644
--- a/source/_posts/2018-09-28.run-node-service-in-background.md
+++ b/src/data/blog/2018-09-28.run-node-service-in-background.md
@@ -1,18 +1,15 @@
---
-title: 后台运行node服务
-comments: true
-fancybox: false
-toc: true
-date: 2018-09-28 09:46:37
+title: "后台运行node服务"
+author: xkcoding
+pubDatetime: 2018-09-28T01:46:37.000Z
+slug: run-node-service-in-background
+featured: false
+draft: false
tags:
-- node
-categories:
-- 技术相关
-- node
-description:
-permalink: run-node-service-in-background
-top:
+ - node
+description: "Node 服务一关终端就停了?用 PM2 守护进程!本文介绍 3 种后台运行 Node 服务的方式,重点推荐 PM2:支持负载均衡、进程监控、日志管理、开机自启。附完整命令速查表:start、stop、restart、logs、monit、save、startup 等,一文掌握 PM2 核心用法。"
---
+
前言
很多时候,我们需要后台运行我们的 node 服务,有3种方式可以实现。
@@ -89,4 +86,4 @@ $ pm2 resurrect # 重新加载保存的应用列表
## 4. 参考
-1. [PM2官方文档](https://pm2.io/doc/en/runtime/overview/?utm_source=pm2&utm_medium=website&utm_campaign=rebranding)
\ No newline at end of file
+1. [PM2官方文档](https://pm2.io/doc/en/runtime/overview/?utm_source=pm2&utm_medium=website&utm_campaign=rebranding)
diff --git a/source/_posts/2018-11-07.spring-boot-start.md b/src/data/blog/2018-11-07.spring-boot-start.md
similarity index 90%
rename from source/_posts/2018-11-07.spring-boot-start.md
rename to src/data/blog/2018-11-07.spring-boot-start.md
index 66318b01..7817b6ec 100644
--- a/source/_posts/2018-11-07.spring-boot-start.md
+++ b/src/data/blog/2018-11-07.spring-boot-start.md
@@ -1,18 +1,15 @@
---
-title: Hello,Spring Boot!
-comments: true
-fancybox: false
-toc: true
-date: 2018-11-07 16:09:07
-tags:
-- spring boot
-categories:
-- 技术相关
-- spring boot
-description:
-permalink: spring-boot-start
-top:
+title: "Hello,Spring Boot!"
+author: xkcoding
+pubDatetime: 2018-11-07T08:09:07.000Z
+slug: spring-boot-start
+featured: false
+draft: false
+tags:
+ - spring-boot
+description: "还在被 Spring 的 XML 配置和依赖版本冲突折磨?Spring Boot 让这一切成为过去!本文从零开始:用 IDEA 的 Spring Initializr 创建项目,理解启动类的 @SpringBootApplication 注解,编写第一个 Controller,运行 main 函数启动内嵌 Tomcat。约定大于配置,开箱即用。"
---
+
Spring Boot 是Spring项目中的一个子工程,与我们所熟知的Spring-framework 同属于spring的产品:
@@ -132,4 +129,4 @@ public class HelloController {
-测试成功了!
\ No newline at end of file
+测试成功了!
diff --git a/source/_posts/2019-01-01.bye2018-hello2019.md b/src/data/blog/2019-01-01.bye2018-hello2019.md
similarity index 88%
rename from source/_posts/2019-01-01.bye2018-hello2019.md
rename to src/data/blog/2019-01-01.bye2018-hello2019.md
index ec5fed55..66f25c9f 100644
--- a/source/_posts/2019-01-01.bye2018-hello2019.md
+++ b/src/data/blog/2019-01-01.bye2018-hello2019.md
@@ -1,17 +1,15 @@
---
-title: 溜了溜了2018,来了来了2019
-comments: true
-fancybox: true
-toc: false
-date: 2019-01-01 00:00:00
-tags:
-- 总结展望
-categories:
-- 总结展望
-description:
-permalink: bye2018-hello2019
-top:
+title: "溜了溜了2018,来了来了2019"
+author: xkcoding
+pubDatetime: 2018-12-31T16:00:00.000Z
+slug: bye2018-hello2019
+featured: false
+draft: false
+tags:
+ - 总结展望
+description: "从北京回杭州,从执行者到产品负责人,2018 是角色转变的一年。工作上开始定 TODO-List、安排团队开发、考虑产品架构;开源上 spring-boot-demo 47 个 demo 持续更新。展望 2019:博客月更 3 篇、GitHub 日日有绿点、希望年底口袋不再空空。技术人的年终总结与自我期许。"
---
+
18年不知不觉已到尽头,但自己总觉得好像18年只过了9个月一样。抓住18年的小尾巴,给自己的2018做个年终总结,为2019做个展望。
@@ -62,4 +60,4 @@ emmm... 这是今年最大的痛,今年更新的博文频率比较低,不找
2. 希望19年博客可以每月保底3篇。
3. 希望19年可以每天都在github留下足迹,不管有没有人star自己的项目,但是我想对已经star的小伙伴和我自己的代码负责到底,这是我的态度。
4. 希望自己可以有些存款,17、18年底都是口袋空空,希望19的年底可以不空了。
-5. 希望自己仍然可以保持对代码的热爱,对技术执着,我一直相信自己的能力,我一直在等待一个合适的机会。
\ No newline at end of file
+5. 希望自己仍然可以保持对代码的热爱,对技术执着,我一直相信自己的能力,我一直在等待一个合适的机会。
diff --git a/source/_posts/2019-01-14.solve-mac-install-minikube-problem.md b/src/data/blog/2019-01-14.solve-mac-install-minikube-problem.md
similarity index 89%
rename from source/_posts/2019-01-14.solve-mac-install-minikube-problem.md
rename to src/data/blog/2019-01-14.solve-mac-install-minikube-problem.md
index e2246f5e..c365820b 100644
--- a/source/_posts/2019-01-14.solve-mac-install-minikube-problem.md
+++ b/src/data/blog/2019-01-14.solve-mac-install-minikube-problem.md
@@ -1,20 +1,16 @@
---
-title: 解决 Mac 安装最新版 minikube 出现的问题
-comments: true
-fancybox: true
-toc: true
-date: 2019-01-14 14:22:11
-tags:
-- kubernetes
-- docker
-- minikube
-categories:
-- 技术相关
-- kubernetes
-description:
-permalink: solve-mac-install-minikube-problem
-top:
+title: "解决 Mac 安装最新版 minikube 出现的问题"
+author: xkcoding
+pubDatetime: 2019-01-14T06:22:11.000Z
+slug: solve-mac-install-minikube-problem
+featured: false
+draft: false
+tags:
+ - kubernetes
+ - docker
+description: "minikube 搭建 K8s 真的傻瓜式吗?至少我遇到了两个大坑:kubectl v1.13 和 minikube 默认的 k8s v1.12 版本不匹配,以及国内网络无法拉取 k8s.gcr.io 镜像。本文记录完整的排坑过程:--kubernetes-version 指定版本、--docker-env 配置代理,VirtualBox NAT 模式下 10.0.2.2 指向宿主机的原理。"
---
+
kubernetes (本文后续简称 k8s ),是一个开源的,用于管理云平台中多个主机上的容器化的应用。Mac 本地运行 k8s 最快的方式,肯定是基于单节点的 minikube 来搭建。人人都说 minikube 搭建 k8s 最简单、快速、只需要傻瓜式输入命令就可以搭建成功,可事实并非如此,比如笔者在搭建的过程中就遇到了 **版本** 和 **代理** 的问题。
下面我将对遇到的问题一一进行解答。
@@ -33,14 +29,13 @@ kubernetes (本文后续简称 k8s ),是一个开源的,用于管理云平
### 2.2. 安装 kubectl
-> 笔者安装的时候,最新版本为 `v1.13.1`
+> 笔者安装的时候,最新版本为 `v1.13.1`
有2种安装方式:
1. 使用 `brew` 安装,`brew install kubernetes-cli`
2. 使用二进制方式安装
-
- 下载二进制文件
`curl -LO https://storage.googleapis.com/kubernetes-release/release/v1.13.1/bin/darwin/amd64/kubectl`
- 修改文件权限
@@ -57,11 +52,10 @@ kubernetes (本文后续简称 k8s ),是一个开源的,用于管理云平
1. 使用 `brew` 安装,`brew cask install minikube`
2. 使用二进制方式安装
-
- 下载二进制文件并修改文件权限
- `curl -Lo minikube https://storage.googleapis.com/minikube/releases/v0.32.0/minikube-darwin-amd64 \ && chmod +x minikube`
+ `curl -Lo minikube https://storage.googleapis.com/minikube/releases/v0.32.0/minikube-darwin-amd64 \ && chmod +x minikube`
- 移动文件到用户 bin 目录
- `sudo cp minikube /usr/local/bin && rm minikube`
+ `sudo cp minikube /usr/local/bin && rm minikube`
## 3. 使用 minikube 启动 k8s
@@ -135,15 +129,15 @@ kubernetes (本文后续简称 k8s ),是一个开源的,用于管理云平
I0114 13:35:19.102307 7316 utils.go:224] > kubeadm join localhost:8443 --token agf5vb.45h4fw6jqy7o2yff --discovery-token-ca-cert-hash sha256:c60ce3456d2ac3225edc119d1792d1b6c20b40b03cf0812ddf8d2fc867f21bf3
Verifying kubelet health ...I0114 13:35:19.134467 7316 ssh_runner.go:137] Run with output: sudo systemctl is-active kubelet
I0114 13:35:19.141912 7316 utils.go:224] > active
-
+
Verifying apiserver health ...I0114 13:35:19.149990 7316 kubeadm.go:99] https://192.168.99.101:8443/healthz response: &{Status:200 OK StatusCode:200 Proto:HTTP/1.1 ProtoMajor:1 ProtoMinor:1 Header:map[Date:[Mon, 14 Jan 2019 05:35:19 GMT] Content-Length:[2] Content-Type:[text/plain; charset=utf-8]] Body:0xc0004b9d00 ContentLength:2 TransferEncoding:[] Close:false Uncompressed:false Trailer:map[] Request:0xc0002da500 TLS:0xc000354000}
Kubectl is now configured to use the cluster.
Loading cached images from config file.
-
-
+
+
Everything looks great. Please enjoy minikube!
```
2. `minikube dashboard` 之后,启动 dashboad 截图:
- 
\ No newline at end of file
+ 
diff --git a/src/data/blog/2019-01-17.solve-nginx-413-problem.md b/src/data/blog/2019-01-17.solve-nginx-413-problem.md
new file mode 100644
index 00000000..2578d84d
--- /dev/null
+++ b/src/data/blog/2019-01-17.solve-nginx-413-problem.md
@@ -0,0 +1,31 @@
+---
+title: "解决 Nginx 413 Request Entity Too Large 问题"
+author: xkcoding
+pubDatetime: 2019-01-17T02:44:21.000Z
+slug: solve-nginx-413-problem
+featured: false
+draft: false
+tags:
+ - nginx
+description: "Spring Boot 文件上传遇到 Nginx 413 Request Entity Too Large?原因是 Nginx 默认限制请求体大小为 1MB。解决方法很简单:在 server 块加一行 client_max_body_size 20M,然后 nginx -s reload 重启即可。一分钟解决的小问题,记录一下备忘。"
+---
+
+使用 Spring Boot 文件上传的时候,前端使用 nginx 代理后端请求,出现如下 **413 Request Entity Too Large** 这个错误。
+
+
+
+
+
+解决方法其实也十分简单,只需要在 nginx 配置文件里添加如下内容,重启 nginx,即可解决。
+
+```nginx
+server {
+ # ...
+ listen 80;
+ server_name localhost;
+ client_max_body_size 20M;
+ # ...
+}
+```
+
+重启 nginx,`nginx -s reload`,大功告成!
diff --git a/source/_posts/2019-01-21.modify-git-commit-timestamp.md b/src/data/blog/2019-01-21.modify-git-commit-timestamp.md
similarity index 50%
rename from source/_posts/2019-01-21.modify-git-commit-timestamp.md
rename to src/data/blog/2019-01-21.modify-git-commit-timestamp.md
index fedce51e..eb98e9cc 100644
--- a/source/_posts/2019-01-21.modify-git-commit-timestamp.md
+++ b/src/data/blog/2019-01-21.modify-git-commit-timestamp.md
@@ -1,18 +1,15 @@
---
-title: 修改 git 提交的时间
-comments: true
-fancybox: false
-toc: true
-date: 2019-01-21 17:10:03
+title: "修改 git 提交的时间"
+author: xkcoding
+pubDatetime: 2019-01-21T09:10:03.000Z
+slug: modify-git-commit-timestamp
+featured: false
+draft: false
tags:
-- git
-categories:
-- 技术相关
-- git
-description:
-permalink: modify-git-commit-timestamp
-top:
+ - git
+description: "想把 Git 提交时间改成跨年那天的 00:00:00?用 git commit --amend --date 就能搞定。本文记录两种场景:修改当前最新提交的时间,以及通过 commit id 修改历史提交的时间。改完记得 git push 同步到远程。GitHub 绿点补救指南。"
---
+
有时候我们需要修改 `git commit` 时间,可以通过下面的方法解决。
修改当前本地提交的commit时间
@@ -27,4 +24,4 @@ git commit --amend --date="2019-01-01T00:00:00+0800" -am ":memo: 更新 TODO.md"
git commit --amend --date="2019-01-01T00:00:00+0800" -C edd2dbbe31fbab492f337628011119493a12a9c6
```
-对于之前已经提交到远程仓库的,需要再 `git push` 一次,即可推送到远程仓库
\ No newline at end of file
+对于之前已经提交到远程仓库的,需要再 `git push` 一次,即可推送到远程仓库
diff --git a/source/_posts/2019-01-30.spring-boot-request-use-enums-params.md b/src/data/blog/2019-01-30.spring-boot-request-use-enums-params.md
similarity index 94%
rename from source/_posts/2019-01-30.spring-boot-request-use-enums-params.md
rename to src/data/blog/2019-01-30.spring-boot-request-use-enums-params.md
index 8bfb2190..f29c9036 100644
--- a/source/_posts/2019-01-30.spring-boot-request-use-enums-params.md
+++ b/src/data/blog/2019-01-30.spring-boot-request-use-enums-params.md
@@ -1,18 +1,15 @@
---
-title: Spring Boot 使用枚举类型作为请求参数
-comments: true
-fancybox: true
-toc: true
-date: 2019-01-30 14:17:37
-tags:
-- spring boot
-categories:
-- 技术相关
-- spring boot
-description:
-permalink: spring-boot-request-use-enums-params
-top:
+title: "Spring Boot 使用枚举类型作为请求参数"
+author: xkcoding
+pubDatetime: 2019-01-30T06:17:37.000Z
+slug: spring-boot-request-use-enums-params
+featured: false
+draft: false
+tags:
+ - spring-boot
+description: "接口参数传 0 或 1,怎么自动转成 GenderEnum.MALE 或 FEMALE?@RequestParam 和 @RequestBody 搞不定,需要自定义 Converter。本文提供完整的通用方案:BaseEnum 接口 + ConverterFactory 工厂模式,一套代码支持所有枚举类型的自动转换,Get/Post 请求都能用。"
---
+
## 1. 场景
在我们实际开发中,枚举类型应用十分广泛,可以避免在项目中定义大量的『魔法值』变量。但是,在web开发中,如何将枚举对象作为请求参数传进Controller,做到类型自动转换?直接使用 `@RequestParam` 和 `@RequestBody` 断然是不够的,这里就需要我们自定义 `Converter` 来实现类型转化了。
@@ -127,7 +124,8 @@ public class EnumTestController {
这么写的时候,gender只能接收到 `MALE` 、`FEMALE` 这样的参数,除此以外,均会报类型不匹配的错误信息,此时是无法处理 `0` 、`1` 这样的参数的。
需求:
-1. 接收到 `MALE` 、`FEMALE` 这样的参数,可以自动转为对应的枚举值;
+
+1. 接收到 `MALE` 、`FEMALE` 这样的参数,可以自动转为对应的枚举值;
2. 接收到 `0` 、`1` 这样的参数,也可以自动转为对应的枚举值。
## 3. 解决
@@ -456,4 +454,3 @@ public class WebMvcConfig implements WebMvcConfigurer {
## 5. 代码地址
https://github.com/xkcoding/owntest/tree/master/spring-boot-test
-
diff --git a/source/_posts/2019-02-04.use-maven-manage-spring-boot-profile.md b/src/data/blog/2019-02-04.use-maven-manage-spring-boot-profile.md
similarity index 88%
rename from source/_posts/2019-02-04.use-maven-manage-spring-boot-profile.md
rename to src/data/blog/2019-02-04.use-maven-manage-spring-boot-profile.md
index 90da0e8e..36ae2a46 100644
--- a/source/_posts/2019-02-04.use-maven-manage-spring-boot-profile.md
+++ b/src/data/blog/2019-02-04.use-maven-manage-spring-boot-profile.md
@@ -1,19 +1,16 @@
---
-title: 使用Maven管理Spring Boot Profile
-comments: true
-fancybox: false
-toc: true
-date: 2019-02-04 09:47:35
-tags:
-- spring boot
-- maven
-categories:
-- 技术相关
-- spring boot
-description:
-permalink: use-maven-manage-spring-boot-profile
-top:
+title: "使用Maven管理Spring Boot Profile"
+author: xkcoding
+pubDatetime: 2019-02-04T01:47:35.000Z
+slug: use-maven-manage-spring-boot-profile
+featured: false
+draft: false
+tags:
+ - spring-boot
+ - java
+description: "每次打包都要手动改 spring.profiles.active?太容易出错了!本文介绍 Maven Profile + @xx@ 变量替换的优雅方案:pom.xml 定义 dev/test/prod 三套环境,application.yml 用 @profileActive@ 动态获取,mvn package -P prod 一键打包。还提供了 shell 脚本和 IDEA 可视化操作两种构建方式。"
---
+
前言
Spring Boot开发的项目中,多环境配置极大的提升了开发效率,避免了因为测试(test)、开发(dev)、生产(prod)导致频繁修改一个配置文件,导致出现配置错误的情况。
@@ -147,4 +144,4 @@ mvn clean package -Dmaven.test.skip=true -P ${profileActive}
1. https://blog.csdn.net/lihe2008125/article/details/50443491
2. https://www.jianshu.com/p/755a57511928
-3. https://segmentfault.com/a/1190000011770028
\ No newline at end of file
+3. https://segmentfault.com/a/1190000011770028
diff --git a/source/_posts/2019-02-13.design-pattern-simple-factory.md b/src/data/blog/2019-02-13.design-pattern-simple-factory.md
similarity index 90%
rename from source/_posts/2019-02-13.design-pattern-simple-factory.md
rename to src/data/blog/2019-02-13.design-pattern-simple-factory.md
index 99a2aa90..65b1498f 100644
--- a/source/_posts/2019-02-13.design-pattern-simple-factory.md
+++ b/src/data/blog/2019-02-13.design-pattern-simple-factory.md
@@ -1,21 +1,18 @@
---
-title: 设计模式之创建型设计模式-简单工厂模式
-comments: true
-fancybox: false
-toc: true
-date: 2019-02-13 10:23:27
+title: "设计模式之创建型设计模式-简单工厂模式"
+author: xkcoding
+pubDatetime: 2019-02-13T02:23:27.000Z
+slug: design-pattern-simple-factory
+featured: false
+draft: false
tags:
-- 设计模式
-categories:
-- 设计模式
-- 创建型设计模式
-description:
-permalink: design-pattern-simple-factory
-top:
+ - 设计模式
+description: "new LemonCandy() 散落在代码各处?简单工厂模式帮你统一管理对象创建!本文用「糖果工厂生产不同口味糖果」的场景,手把手实现简单工厂:抽象糖果类 → 具体糖果实现 → 工厂类封装创建逻辑。还有两种工厂方法实现:字符串参数和 Class 参数反射创建。附 Slf4j LoggerFactory 源码分析。"
---
+
## 1. 模式简介
-简单工厂模式(Simple Factory Pattern):它属于`创建型设计模式`,`不属于23种设计模式`。在简单工厂模式中,可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。本文将使用 ***糖果工厂生产不同口味的糖果*** 这个实际场景,用代码来实现 **简单工厂模式**。
+简单工厂模式(Simple Factory Pattern):它属于`创建型设计模式`,`不属于23种设计模式`。在简单工厂模式中,可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。本文将使用 **_糖果工厂生产不同口味的糖果_** 这个实际场景,用代码来实现 **简单工厂模式**。
@@ -233,4 +230,3 @@ public class PatternTest {
## 8. 完整代码地址
https://github.com/xkcoding/design-pattern/tree/master/src/main/java/com/xkcoding/design/pattern/creational/simplefactory
-
diff --git a/source/_posts/2019-02-15.design-pattern-factory-method.md b/src/data/blog/2019-02-15.design-pattern-factory-method.md
similarity index 90%
rename from source/_posts/2019-02-15.design-pattern-factory-method.md
rename to src/data/blog/2019-02-15.design-pattern-factory-method.md
index 325145b9..f7fec76e 100644
--- a/source/_posts/2019-02-15.design-pattern-factory-method.md
+++ b/src/data/blog/2019-02-15.design-pattern-factory-method.md
@@ -1,21 +1,18 @@
---
-title: 设计模式之创建型设计模式-工厂方法模式
-comments: true
-fancybox: false
-toc: true
-date: 2019-02-15 09:00:54
+title: "设计模式之创建型设计模式-工厂方法模式"
+author: xkcoding
+pubDatetime: 2019-02-15T01:00:54.000Z
+slug: design-pattern-factory-method
+featured: false
+draft: false
tags:
-- 设计模式
-categories:
-- 设计模式
-- 创建型设计模式
-description:
-permalink: design-pattern-factory-method
-top:
+ - 设计模式
+description: "简单工厂的 if-else 越写越长?工厂方法模式来解耦!核心思想:把具体类的实例化延迟到工厂子类,新增产品只需新增工厂类,不用改原有代码。本文继续用糖果工厂的例子,对比简单工厂 vs 工厂方法的 UML 图和代码实现。附 JDK Calendar.getInstance() 源码分析,看看大佬怎么用。"
---
+
## 1. 模式简介
-工厂方法模式(Factory Method Pattern):又称为工厂模式,属于`创建型设计模式`。在工厂方法模式中,工厂父类负责定义创建对象的公共接口,工厂子类负责生成具体的对象,这样做的目的是将具体类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体对象。本文将使用 ***糖果工厂生产不同口味的糖果*** 这个实际场景,用代码来实现 **工厂方法模式**。
+工厂方法模式(Factory Method Pattern):又称为工厂模式,属于`创建型设计模式`。在工厂方法模式中,工厂父类负责定义创建对象的公共接口,工厂子类负责生成具体的对象,这样做的目的是将具体类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体对象。本文将使用 **_糖果工厂生产不同口味的糖果_** 这个实际场景,用代码来实现 **工厂方法模式**。
@@ -261,4 +258,4 @@ public class PatternTest {
## 8. 完整代码地址
-https://github.com/xkcoding/design-pattern/tree/master/src/main/java/com/xkcoding/design/pattern/creational/factorymethod
\ No newline at end of file
+https://github.com/xkcoding/design-pattern/tree/master/src/main/java/com/xkcoding/design/pattern/creational/factorymethod
diff --git a/source/_posts/2019-02-18.use-mybatis-dynamic-sql-to-build-common-custom-advance-query.md b/src/data/blog/2019-02-18.use-mybatis-dynamic-sql-to-build-common-custom-advance-query.md
similarity index 89%
rename from source/_posts/2019-02-18.use-mybatis-dynamic-sql-to-build-common-custom-advance-query.md
rename to src/data/blog/2019-02-18.use-mybatis-dynamic-sql-to-build-common-custom-advance-query.md
index 69df75c5..71add821 100644
--- a/source/_posts/2019-02-18.use-mybatis-dynamic-sql-to-build-common-custom-advance-query.md
+++ b/src/data/blog/2019-02-18.use-mybatis-dynamic-sql-to-build-common-custom-advance-query.md
@@ -1,18 +1,15 @@
---
-title: 使用Mybatis动态SQL构建通用自定义高级查询
-comments: true
-fancybox: false
-toc: true
-date: 2019-02-18 10:36:29
+title: "使用Mybatis动态SQL构建通用自定义高级查询"
+author: xkcoding
+pubDatetime: 2019-02-18T02:36:29.000Z
+slug: use-mybatis-dynamic-sql-to-build-common-custom-advance-query
+featured: false
+draft: false
tags:
-- mybatis
-categories:
-- 代码片段
-- mybatis
-description:
-permalink: use-mybatis-dynamic-sql-to-build-common-custom-advance-query
-top:
+ - mybatis
+description: "每个 Mapper.xml 都写一堆 if 条件太烦了!本文提供通用高级查询方案:定义 AdvanceQuery 模型封装 key/op/value,SQL 片段支持 eq/gte/lte/between/in/notin/like 等 9 种操作符。前端传 JSON 数组,后端动态拼装 WHERE 条件。一套模板复用所有查询,告别重复代码。附完整 XML 模板和 PostMan 测试示例。"
---
+
前言
动态SQL是Mybatis中最强大的特性之一。实际开发中,数据的查询条件是动态多变的,因此动态SQL可以很方便的构建多条件的 `where` 语句的拼装。但是where条件里的查询条件跟表中的字段还是息息相关,我们在编写`*Mapper.xml` 文件的时候,还是需要在 `` 节点里写编写很多的 `` 条件。程序猿都是一种爱偷懒的物种,于是乎,便有了此文的主题『使用Mybatis动态SQL构建通用自定义高级查询』。话不多说,进入正题。
@@ -231,21 +228,21 @@ public interface UserMapper extends MyMapper {
```json
[
- {
- "key":"username",
- "op":"like",
- "value":["xk","coding"]
- },
- {
- "key":"gender",
- "op":"eq",
- "value":[1]
- },
- {
- "key":"birthday",
- "op":"between",
- "value":["1994-11-01 00:00:00","1994-12-01 00:00:00"]
- }
+ {
+ "key": "username",
+ "op": "like",
+ "value": ["xk", "coding"]
+ },
+ {
+ "key": "gender",
+ "op": "eq",
+ "value": [1]
+ },
+ {
+ "key": "birthday",
+ "op": "between",
+ "value": ["1994-11-01 00:00:00", "1994-12-01 00:00:00"]
+ }
]
```
@@ -253,12 +250,12 @@ public interface UserMapper extends MyMapper {
| 内容 | 含义 | value值备注 |
| :-----: | :------: | :----------------------------------------------------------: |
-| eq | 等于 | 有且只有 **1** 个 |
-| gte | 大于等于 | 有且只有 **1** 个 |
-| lte | 小于等于 | 有且只有 **1** 个 |
-| between | 介于 | 有且只有 **2** 个 |
-| in | 包含 | 大于等于 **1** 个 |
-| notin | 不包含 | 大于等于 **1** 个 |
+| eq | 等于 | 有且只有 **1** 个 |
+| gte | 大于等于 | 有且只有 **1** 个 |
+| lte | 小于等于 | 有且只有 **1** 个 |
+| between | 介于 | 有且只有 **2** 个 |
+| in | 包含 | 大于等于 **1** 个 |
+| notin | 不包含 | 大于等于 **1** 个 |
| no | 不存在 | 空数组 |
| yes | 存在 | 空数组 |
| like | 模糊 | 大于等于 **1** 个,多个 value 时,会使用 **OR** 构建查询条件 |
@@ -272,4 +269,3 @@ public interface UserMapper extends MyMapper {
## 6. 参考
[MyBatis 3 | 动态SQL - MyBatis.org](http://www.mybatis.org/mybatis-3/zh/dynamic-sql.html)
-
diff --git a/source/_posts/2019-03-12.scaffold-doc.md b/src/data/blog/2019-03-12.scaffold-doc.md
similarity index 91%
rename from source/_posts/2019-03-12.scaffold-doc.md
rename to src/data/blog/2019-03-12.scaffold-doc.md
index 709729b2..87aaf43a 100644
--- a/source/_posts/2019-03-12.scaffold-doc.md
+++ b/src/data/blog/2019-03-12.scaffold-doc.md
@@ -1,20 +1,16 @@
---
-title: scaffold 组件相关文档
-comments: true
-fancybox: false
-toc: true
-date: 2019-03-12 09:51:34
-tags:
-- spring boot
-- scaffold
-- 开源
-categories:
-- 开源
-- scaffold
-description:
-permalink: scaffold-doc
-top:
+title: "scaffold 组件相关文档"
+author: xkcoding
+pubDatetime: 2019-03-12T01:51:34.000Z
+slug: scaffold-doc
+featured: false
+draft: false
+tags:
+ - spring-boot
+ - 开源
+description: "Spring Boot 项目总是重复造轮子?scaffold 开源脚手架帮你搞定!10 个模块覆盖常见需求:launcher 多环境启动器、common 统一返回格式、log 操作日志注解、swagger 自动装配、web 全局异常处理、code 验证码、notification 钉钉/邮件/短信通知、codegen 代码生成器。开箱即用,让你专注业务逻辑。"
---
+
前言
scaffold: 脚手架的意思,是一款封装了一些通用组件的,让Spring Boot快速开发的工具集。
@@ -22,7 +18,7 @@ scaffold: 脚手架的意思,是一款封装了一些通用组件的,让Spri
## 1. Scaffold组件图谱
-
+
## 2. 项目信息
@@ -58,7 +54,7 @@ scaffold
```
-## 4. 各个模块功能介绍及使用方法
+## 4. 各个模块功能介绍及使用方法
### 4.1. scaffold-bom 版本依赖管理
@@ -224,7 +220,7 @@ public interface LauncherService {
> 推荐使用 `scaffold-bom` 管理 `scaffold` 模块版本,使用方法[请看文档](#4-1-scaffold-bom-%E7%89%88%E6%9C%AC%E4%BE%9D%E8%B5%96%E7%AE%A1%E7%90%86)
-`scaffold-common ` 最新版本: [ ](https://maven-badges.herokuapp.com/maven-central/com.xkcoding/scaffold-common )
+`scaffold-common ` 最新版本: [ ](https://maven-badges.herokuapp.com/maven-central/com.xkcoding/scaffold-common)
```xml
@@ -303,7 +299,7 @@ public interface LauncherService {
> 推荐使用 `scaffold-bom` 管理 `scaffold` 模块版本,使用方法[请看文档](#4-1-scaffold-bom-%E7%89%88%E6%9C%AC%E4%BE%9D%E8%B5%96%E7%AE%A1%E7%90%86)
-`scaffold-log ` 最新版本: [ ](https://maven-badges.herokuapp.com/maven-central/com.xkcoding/scaffold-log )
+`scaffold-log ` 最新版本: [ ](https://maven-badges.herokuapp.com/maven-central/com.xkcoding/scaffold-log)
```xml
@@ -405,7 +401,7 @@ public class LogConfig {
> 推荐使用 `scaffold-bom` 管理 `scaffold` 模块版本,使用方法[请看文档](#4-1-scaffold-bom-%E7%89%88%E6%9C%AC%E4%BE%9D%E8%B5%96%E7%AE%A1%E7%90%86)
-`scaffold-swagger ` 最新版本: [ ](https://maven-badges.herokuapp.com/maven-central/com.xkcoding/scaffold-swagger )
+`scaffold-swagger ` 最新版本: [ ](https://maven-badges.herokuapp.com/maven-central/com.xkcoding/scaffold-swagger)
```xml
@@ -452,7 +448,7 @@ swagger:
> 推荐使用 `scaffold-bom` 管理 `scaffold` 模块版本,使用方法[请看文档](#4-1-scaffold-bom-%E7%89%88%E6%9C%AC%E4%BE%9D%E8%B5%96%E7%AE%A1%E7%90%86)
-`scaffold-test ` 最新版本: [ ](https://maven-badges.herokuapp.com/maven-central/com.xkcoding/scaffold-test )
+`scaffold-test ` 最新版本: [ ](https://maven-badges.herokuapp.com/maven-central/com.xkcoding/scaffold-test)
```xml
@@ -513,7 +509,7 @@ public class DemoTests extends ScaffoldBaseTest {
> 推荐使用 `scaffold-bom` 管理 `scaffold` 模块版本,使用方法[请看文档](#4-1-scaffold-bom-%E7%89%88%E6%9C%AC%E4%BE%9D%E8%B5%96%E7%AE%A1%E7%90%86)
-`scaffold-web ` 最新版本: [ ](https://maven-badges.herokuapp.com/maven-central/com.xkcoding/scaffold-web )
+`scaffold-web ` 最新版本: [ ](https://maven-badges.herokuapp.com/maven-central/com.xkcoding/scaffold-web)
```xml
@@ -535,7 +531,7 @@ public class DemoTests extends ScaffoldBaseTest {
##### ③ 监控打印请求
```bash
-2019-03-13 15:38:11.650 INFO 6846 --- [ XNIO-1 task-2] com.xkcoding.web.logger.RequestLogAspect : RequestLogAspect.java:157 -
+2019-03-13 15:38:11.650 INFO 6846 --- [ XNIO-1 task-2] com.xkcoding.web.logger.RequestLogAspect : RequestLogAspect.java:157 -
================ Request Start ================
===> POST: /demo/ Parameters: {"name":"","id":0,"age":0,"email":""}
===headers=== Origin : http://localhost:8080
@@ -571,7 +567,7 @@ public class DemoTests extends ScaffoldBaseTest {
> 推荐使用 `scaffold-bom` 管理 `scaffold` 模块版本,使用方法[请看文档](#4-1-scaffold-bom-%E7%89%88%E6%9C%AC%E4%BE%9D%E8%B5%96%E7%AE%A1%E7%90%86)
-`scaffold-code ` 最新版本: [ ](https://maven-badges.herokuapp.com/maven-central/com.xkcoding/scaffold-code )
+`scaffold-code ` 最新版本: [ ](https://maven-badges.herokuapp.com/maven-central/com.xkcoding/scaffold-code)
```xml
@@ -671,7 +667,7 @@ public class DemoController {
> 推荐使用 `scaffold-bom` 管理 `scaffold` 模块版本,使用方法[请看文档](#4-1-scaffold-bom-%E7%89%88%E6%9C%AC%E4%BE%9D%E8%B5%96%E7%AE%A1%E7%90%86)
-`scaffold-notification ` 最新版本: [ ](https://maven-badges.herokuapp.com/maven-central/com.xkcoding/scaffold-notification )
+`scaffold-notification ` 最新版本: [ ](https://maven-badges.herokuapp.com/maven-central/com.xkcoding/scaffold-notification)
```xml
@@ -724,17 +720,17 @@ spring:
##### ③ 自定义配置项
-| 配置项 | 默认值 | 说明 |
-| --------------------------------------- | ----------------- | ------------------------------------------------------------ |
-| scaffold.notification.dingtalk.enabled | false | 是否启用钉钉机器人通知 |
-| scaffold.notification.dingtalk.webhook | 无 | 钉钉机器人webhook地址 |
-| scaffold.notification.aliyun.enabled | false | 是否启用阿里大鱼短信通知 |
-| scaffold.notification.aliyun.access-key | 无 | 阿里云 AccessKey |
-| scaffold.notification.aliyun.secret-key | 无 | 阿里云 AccessSecret |
+| 配置项 | 默认值 | 说明 |
+| --------------------------------------- | ----------------- | ------------------------------------------------------------------------------------------------------------- |
+| scaffold.notification.dingtalk.enabled | false | 是否启用钉钉机器人通知 |
+| scaffold.notification.dingtalk.webhook | 无 | 钉钉机器人webhook地址 |
+| scaffold.notification.aliyun.enabled | false | 是否启用阿里大鱼短信通知 |
+| scaffold.notification.aliyun.access-key | 无 | 阿里云 AccessKey |
+| scaffold.notification.aliyun.secret-key | 无 | 阿里云 AccessSecret |
| scaffold.notification.aliyun.channels | 无 | 模板与模板CODE的映射关系,参见阿里大鱼短信服务的模板管理,格式为:`模版名称:模版CODE`,注意模板名称不能为中文 |
-| scaffold.notification.email.enabled | false | 是否启用邮箱通知,如果启用,`一定需要`额外配置邮箱信息 |
-| scaffold.notification.email.prefix | classpath:/email/ | 邮件模板位置 |
-| scaffold.notification.email.suffix | .html | 邮件模板后缀 |
+| scaffold.notification.email.enabled | false | 是否启用邮箱通知,如果启用,`一定需要`额外配置邮箱信息 |
+| scaffold.notification.email.prefix | classpath:/email/ | 邮件模板位置 |
+| scaffold.notification.email.suffix | .html | 邮件模板后缀 |
##### ④ 示例代码
@@ -883,54 +879,72 @@ public class NotificationTest extends ScaffoldBaseTest {
```html
-
-
+
+
SpringBootDemo(入门SpringBoot的首选Demo)
-
-
-
-
-
欢迎使用 - Powered By
-
-
-
- spring-boot-demo,入门Spring Boot的首选Demo!:)
+
+
+
+
+
+
+
+ 欢迎使用 - Powered By
+
+
+
+
-
+
+
如果对你有帮助,请任意打赏
-
-
+
+
-
-
-
-
-
微信打赏
+
+
+
-
-
-
-
支付宝打赏
+
微信打赏
+
+
+
+
+
支付宝打赏
+
+
-
-
-
+
```
@@ -971,7 +985,7 @@ public class NotificationTest extends ScaffoldBaseTest {
> 推荐使用 `scaffold-bom` 管理 `scaffold` 模块版本,使用方法[请看文档](#4-1-scaffold-bom-%E7%89%88%E6%9C%AC%E4%BE%9D%E8%B5%96%E7%AE%A1%E7%90%86)
-
`scaffold-codegen ` 最新版本: [ ](https://maven-badges.herokuapp.com/maven-central/com.xkcoding/scaffold-codegen )
+
`scaffold-codegen ` 最新版本: [ ](https://maven-badges.herokuapp.com/maven-central/com.xkcoding/scaffold-codegen)
```xml
@@ -988,7 +1002,7 @@ public class NotificationTest extends ScaffoldBaseTest {
| scaffold.codegen.author | com.xkcoding | 基础包名 |
| scaffold.codegen.base-package-name | codegen | 模块名 |
| scaffold.codegen.module-name | Yangkai.Shen | 作者 |
-| scaffold.codegen.table-prefix | tb_ | 表前缀(类名不会包含表前缀) |
+| scaffold.codegen.table-prefix | tb\_ | 表前缀(类名不会包含表前缀) |
##### ③ 在application.yml中添加数据源信息
@@ -1010,4 +1024,4 @@ spring:
## TODO
- **scaffold-cache** 通用基于redis或者ehcache的缓存配置,设计缓存key,保证全系统key统一
-- **scaffold-auth** 通用认证授权配置
\ No newline at end of file
+- **scaffold-auth** 通用认证授权配置
diff --git a/source/_posts/2019-04-24.use-vagrant-build-linux-virtual-machine.md b/src/data/blog/2019-04-24.use-vagrant-build-linux-virtual-machine.md
similarity index 94%
rename from source/_posts/2019-04-24.use-vagrant-build-linux-virtual-machine.md
rename to src/data/blog/2019-04-24.use-vagrant-build-linux-virtual-machine.md
index 4da69e34..8146fc9c 100644
--- a/source/_posts/2019-04-24.use-vagrant-build-linux-virtual-machine.md
+++ b/src/data/blog/2019-04-24.use-vagrant-build-linux-virtual-machine.md
@@ -1,20 +1,17 @@
---
-title: Mac下使用Vagrant快速搭建Centos/7虚拟机
-comments: true
-fancybox: false
-toc: true
-date: 2019-04-24 16:52:23
+title: "Mac下使用Vagrant快速搭建Centos/7虚拟机"
+author: xkcoding
+pubDatetime: 2019-04-24T08:52:23.000Z
+slug: use-vagrant-build-linux-virtual-machine
+featured: false
+draft: false
tags:
-- kubernetes
-- vagrant
-- Mac
-categories:
-- 技术相关
-- vagrant
-description:
-permalink: use-vagrant-build-linux-virtual-machine
-top:
+ - kubernetes
+ - devops
+ - mac
+description: "本地搭建 K8s 集群?先来几台 CentOS 虚拟机!Vagrant + VirtualBox 比手动安装快 10 倍。本文从零开始:box 镜像下载、vagrant init/up 基础命令、共享文件夹和网络代理配置。最后附一个完整 Vagrantfile 案例:一键创建 3 节点集群,自动安装 Docker 并设置开机启动。"
---
+
前言
日常开发过程中,为了保证开发环境的统一并且又不想污染我们本机的环境,通常我们有 2 种方式可以做到,一种是状态火热的docker虚拟化技术,另一种就是我们经久不衰的虚拟机了。本文介绍在 Mac 下使用 Vagrant 快速搭建Centos7 虚拟机。
@@ -85,7 +82,7 @@ vagrant box add <虚拟机名> # 添加box的操作
vagrant up # 启动虚拟机的操作
vagrant ssh # 登录拟机的操作,也可以指定hostname登陆
vagrant box list # 显示当前已经添加的box列表
-vagrant box remove 名称 # 删除相应的box
+vagrant box remove 名称 # 删除相应的box
vagrant destroy # 停止当前正在运行的虚拟机并销毁所有创建的资源
vagrant halt # 关机
vagrant status # 获取当前虚拟机的状态,也可以查看指定hostname
@@ -253,7 +250,7 @@ config.vm.provision "shell", privileged: true, path: "./setup.sh"
### 5.4. 如何配置聚群化环境
-#### 5.4.1. 设置硬件参数
+#### 5.4.1. 设置硬件参数
设置虚拟机内存大小和CPU核数,在 `Vagrantfile` 中配置如下信息即可:
@@ -264,7 +261,7 @@ config.vm.provision "shell", privileged: true, path: "./setup.sh"
end
```
-#### 5.4.2. 设置hostname
+#### 5.4.2. 设置hostname
在 `Vagrantfile` 中配置如下信息即可:
@@ -272,7 +269,7 @@ config.vm.provision "shell", privileged: true, path: "./setup.sh"
config.vm.hostname = "master1"
```
-#### 5.4.3. 设置静态IP
+#### 5.4.3. 设置静态IP
在 `Vagrantfile` 中配置如下信息即可:
@@ -282,7 +279,7 @@ config.vm.network "private_network", ip: "192.168.205.10"
### 5.5. 可用插件
-从上面的 *`共享宿主机文件夹`*、*`网络代理`* 等高阶操作,我们可以发现,Vagrant 拥有丰富的插件可以帮助我们完成很多个性化需求,对于这些插件,官方提供了一个地址,专门整理了大量的 Vagrant 插件。
+从上面的 _`共享宿主机文件夹`_、_`网络代理`_ 等高阶操作,我们可以发现,Vagrant 拥有丰富的插件可以帮助我们完成很多个性化需求,对于这些插件,官方提供了一个地址,专门整理了大量的 Vagrant 插件。
> 插件列表地址:https://github.com/hashicorp/vagrant/wiki/Available-Vagrant-Plugins
@@ -338,7 +335,7 @@ Vagrant.configure("2") do |config|
# 配置 IP
config.vm.network :private_network, ip: opts[:eth1]
-
+
# 设置网络代理(需要安装 vagrant-proxyconf 插件)
if Vagrant.has_plugin?("vagrant-proxyconf")
config.proxy.http = "http://192.168.205.1:1087/"
@@ -348,7 +345,7 @@ Vagrant.configure("2") do |config|
# 设置启动后初始化脚本,注意 privileged: true,表示以 sudo 方式运行
config.vm.provision "shell", privileged: true, path: "./setup.sh"
-
+
# 设置宿主机共享文件夹(需要安装 vagrant-vbguest 插件)
# config.vm.synced_folder "../data", "/vagrant_data"
end
@@ -373,7 +370,7 @@ echo "change yum repo -> aliyun repo"
sudo yum install -y wget yum-utils
sudo cp /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bak
sudo wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
-sudo wget -P /etc/yum.repos.d/ http://mirrors.aliyun.com/repo/epel-7.repo
+sudo wget -P /etc/yum.repos.d/ http://mirrors.aliyun.com/repo/epel-7.repo
sudo yum clean all
sudo yum makecache
@@ -402,4 +399,3 @@ sudo systemctl enable docker
- [Vagrant 笔记](https://networm.me/2017/01/01/vagrant-note/)
- [使用vagrant和vitrualBox搭建虚拟开发环境](https://zhuyasen.com/post/vv_evn.html)
- [Vagrant搭建虚拟化开发环境](https://zhuanlan.zhihu.com/p/27793159)
-
diff --git a/source/_posts/2019-05-22.spring-boot-login-with-oauth.md b/src/data/blog/2019-05-22.spring-boot-login-with-oauth.md
similarity index 88%
rename from source/_posts/2019-05-22.spring-boot-login-with-oauth.md
rename to src/data/blog/2019-05-22.spring-boot-login-with-oauth.md
index 19d7204c..d8008b4d 100644
--- a/source/_posts/2019-05-22.spring-boot-login-with-oauth.md
+++ b/src/data/blog/2019-05-22.spring-boot-login-with-oauth.md
@@ -1,20 +1,17 @@
---
-title: Spring Boot快速集成第三方登录功能
-comments: true
-fancybox: false
-toc: true
-date: 2019-05-22 16:21:43
-tags:
-- spring boot
-- JustAuth
-- 开源
-categories:
-- 开源
-- JustAuth
-description:
-permalink: spring-boot-login-with-oauth
-top:
+title: "Spring Boot快速集成第三方登录功能"
+author: xkcoding
+pubDatetime: 2019-05-22T08:21:43.000Z
+slug: spring-boot-login-with-oauth
+featured: false
+draft: false
+tags:
+ - spring-boot
+ - justauth
+ - 开源
+description: "QQ、微信、GitHub、Google、微软……7 种第三方登录一次搞定!使用 JustAuth + justauth-spring-boot-starter 快速集成,yml 配置 + 一个 Controller 就能跑。本文还手把手教你搭建 frp 内网穿透环境、各平台应用申请流程,从零到能跑的完整指南。"
---
+
前言
> 此 demo 主要演示 Spring Boot 项目如何使用 **[史上最全的第三方登录工具 - JustAuth](https://github.com/zhangyd-c/JustAuth)** 实现第三方登录,包括QQ登录、GitHub登录、微信登录、谷歌登录、微软登录、小米登录、企业微信登录。
>
@@ -70,9 +67,9 @@ https://github.com/xkcoding/act-justauth-demo
```shell
$ cd frp_0.27.0_linux_amd64
$ vim frps.ini
-
- [common]
- bind_port = 7100
+
+ [common]
+ bind_port = 7100
vhost_http_port = 7200
```
@@ -106,11 +103,11 @@ https://github.com/xkcoding/act-justauth-demo
```shell
$ cd frp_0.27.0_darwin_amd64
$ vim frpc.ini
-
+
[common]
server_addr = 120.92.169.103
server_port = 7100
-
+
[web]
type = http
local_port = 8080
@@ -139,25 +136,25 @@ nginx 的搭建就不在此赘述了,只说配置
```nginx
server {
listen 80;
- server_name oauth.xkcoding.com;
-
+ server_name oauth.xkcoding.com;
+
location / {
proxy_pass http://127.0.0.1:7200;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Proto $scheme;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_buffering off;
- sendfile off;
- proxy_max_temp_file_size 0;
- client_max_body_size 10m;
- client_body_buffer_size 128k;
- proxy_connect_timeout 90;
- proxy_send_timeout 90;
- proxy_read_timeout 90;
- proxy_temp_file_write_size 64k;
- proxy_http_version 1.1;
- proxy_request_buffering off;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_buffering off;
+ sendfile off;
+ proxy_max_temp_file_size 0;
+ client_max_body_size 10m;
+ client_body_buffer_size 128k;
+ proxy_connect_timeout 90;
+ proxy_send_timeout 90;
+ proxy_read_timeout 90;
+ proxy_temp_file_write_size 64k;
+ proxy_http_version 1.1;
+ proxy_request_buffering off;
}
}
```
@@ -187,7 +184,7 @@ $ nginx -s reload
#### 1.5.1. QQ互联平台申请
-1. 前往 https://connect.qq.com/
+1. 前往 https://connect.qq.com/
2. 申请开发者
@@ -267,7 +264,7 @@ $ nginx -s reload
## 2. 主要代码
> 代码地址:https://github.com/xkcoding/spring-boot-demo/tree/master/spring-boot-demo-social
-> 本 demo 采用 Redis 缓存 state,所以请准备 Redis 环境,如果没有 Redis 环境,可以将配置文件的缓存配置为
+> 本 demo 采用 Redis 缓存 state,所以请准备 Redis 环境,如果没有 Redis 环境,可以将配置文件的缓存配置为
>
> ```yaml
> justauth:
@@ -426,7 +423,7 @@ justauth:
agent-id: 1*******2
cache:
type: redis
- prefix: 'SOCIAL::STATE::'
+ prefix: "SOCIAL::STATE::"
timeout: 1h
```
@@ -528,4 +525,4 @@ public class OauthController {
8. GitHub第三方登录文档:https://developer.github.com/apps/building-oauth-apps/
9. 谷歌Oauth2文档:https://developers.google.com/identity/protocols/OpenIDConnect
10. 微软Oauth2文档:https://docs.microsoft.com/zh-cn/graph/auth-v2-user
-11. 小米开放平台账号服务文档:https://dev.mi.com/console/doc/detail?pId=707
\ No newline at end of file
+11. 小米开放平台账号服务文档:https://dev.mi.com/console/doc/detail?pId=707
diff --git a/source/_posts/2019-07-17.jfinal-login-with-oauth.md b/src/data/blog/2019-07-17.jfinal-login-with-oauth.md
similarity index 94%
rename from source/_posts/2019-07-17.jfinal-login-with-oauth.md
rename to src/data/blog/2019-07-17.jfinal-login-with-oauth.md
index 91add0b0..b5d31423 100644
--- a/source/_posts/2019-07-17.jfinal-login-with-oauth.md
+++ b/src/data/blog/2019-07-17.jfinal-login-with-oauth.md
@@ -1,20 +1,17 @@
---
-title: JFinal 快速集成第三方登录功能
-comments: true
-fancybox: false
-toc: true
-date: 2019-07-17 18:30:46
-tags:
-- JFinal
-- JustAuth
-- 开源
-categories:
-- 开源
-- JustAuth
-description:
-permalink: jfinal-login-with-oauth
-top:
+title: "JFinal 快速集成第三方登录功能"
+author: xkcoding
+pubDatetime: 2019-07-17T10:30:46.000Z
+slug: jfinal-login-with-oauth
+featured: false
+draft: false
+tags:
+ - java
+ - justauth
+ - 开源
+description: "不用 Spring Boot 也能快速接入第三方登录!本文演示 JFinal 框架如何通过 JustAuth 集成 QQ、GitHub、小米登录。踩坑记录:JFinal 路由不支持 /{type}/callback 格式,回调地址要改成 /callback/{type}。附完整配置文件和 Controller 代码。"
---
+
前言
> 此 demo 主要为了演示 Jfinal 如何通过 JustAuth 快速集成第三方平台的登录,如果有小伙伴是基于 Spring Boot 的可以参考这个 [**` demo`**](https://github.com/xkcoding/spring-boot-demo/tree/master/spring-boot-demo-social)
>
@@ -318,4 +315,4 @@ public class DemoApplication {
3. JustAuth 项目地址:https://github.com/zhangyd-c/JustAuth
4. QQ互联文档:http://wiki.connect.qq.com/%E5%87%86%E5%A4%87%E5%B7%A5%E4%BD%9C_oauth2-0
5. GitHub第三方登录文档:https://developer.github.com/apps/building-oauth-apps/
-6. 小米开放平台账号服务文档:https://dev.mi.com/console/doc/detail?pId=707
\ No newline at end of file
+6. 小米开放平台账号服务文档:https://dev.mi.com/console/doc/detail?pId=707
diff --git a/source/_posts/2019-08-06.use-justauth-integration-wechat-enterprise.md b/src/data/blog/2019-08-06.use-justauth-integration-wechat-enterprise.md
similarity index 87%
rename from source/_posts/2019-08-06.use-justauth-integration-wechat-enterprise.md
rename to src/data/blog/2019-08-06.use-justauth-integration-wechat-enterprise.md
index 4aa3731d..0ceb30f5 100644
--- a/source/_posts/2019-08-06.use-justauth-integration-wechat-enterprise.md
+++ b/src/data/blog/2019-08-06.use-justauth-integration-wechat-enterprise.md
@@ -1,20 +1,17 @@
---
-title: 使用JustAuth集成企业微信
-comments: true
-fancybox: false
-toc: true
-date: 2019-08-06 16:51:57
-tags:
-- spring boot
-- JustAuth
-- 开源
-categories:
-- 开源
-- JustAuth
-description:
-permalink: use-justauth-integration-wechat-enterprise
-top:
+title: "使用JustAuth集成企业微信"
+author: xkcoding
+pubDatetime: 2019-08-06T08:51:57.000Z
+slug: use-justauth-integration-wechat-enterprise
+featured: false
+draft: false
+tags:
+ - spring-boot
+ - justauth
+ - 开源
+description: "企业微信扫码登录怎么接?JustAuth 新增企业微信支持!本文详解:注册企业微信 → 创建应用 → 配置授权回调域 → 获取 4 个关键参数(client-id、client-secret、redirect-uri、agent-id)。最后用 justauth-spring-boot-starter 三步完成集成,附效果截图。"
---
+
JustAuth 又㕛叒叕添加新平台啦~
此次更新带来的是 `企业微信`,本文将带领大家使用 JustAuth 快速集成 `企业微信` 的第三方登录。
@@ -63,7 +60,7 @@ JustAuth 又㕛叒叕添加新平台啦~
#### 3.2.1. agent-id 及 client-secret 信息
-`agent-id` 和 `client-secret`这两个信息均在 `应用管理` 里可以查看
+`agent-id` 和 `client-secret`这两个信息均在 `应用管理` 里可以查看

@@ -169,4 +166,4 @@ public class TestController {
- 获取用户身份:https://open.work.weixin.qq.com/api/doc#90000/90135/91437
-- 用户身份详细信息:https://open.work.weixin.qq.com/api/doc#90000/90135/90196
\ No newline at end of file
+- 用户身份详细信息:https://open.work.weixin.qq.com/api/doc#90000/90135/90196
diff --git a/source/_posts/2019-08-10.design-pattern-abstract-factory.md b/src/data/blog/2019-08-10.design-pattern-abstract-factory.md
similarity index 88%
rename from source/_posts/2019-08-10.design-pattern-abstract-factory.md
rename to src/data/blog/2019-08-10.design-pattern-abstract-factory.md
index 20e92fd3..58e95da3 100644
--- a/source/_posts/2019-08-10.design-pattern-abstract-factory.md
+++ b/src/data/blog/2019-08-10.design-pattern-abstract-factory.md
@@ -1,18 +1,15 @@
---
-title: 设计模式之创建型设计模式-抽象工厂模式
-comments: true
-fancybox: false
-toc: true
-date: 2019-08-10 11:34:16
+title: "设计模式之创建型设计模式-抽象工厂模式"
+author: xkcoding
+pubDatetime: 2019-08-10T03:34:16.000Z
+slug: design-pattern-abstract-factory
+featured: false
+draft: false
tags:
-- 设计模式
-categories:
-- 设计模式
-- 创建型设计模式
-description:
-permalink: design-pattern-abstract-factory
-top:
+ - 设计模式
+description: "简单工厂生产单一产品,工厂方法还是单一产品,抽象工厂才是「产品族」!本文用科技公司卖手机+笔记本+耳机的场景讲解:小米、华为、苹果三家公司,每家都有自己的产品线。TechFactory 抽象工厂定义 sellPhone/sellLaptop/sellEarPhone 三个方法,子类各自实现。附 Spring AbstractBeanFactory 源码分析。"
---
+
## 1. 模式简介
提供一个创建一系列相关或者相互依赖对象的接口,无须指定具体的类,属于`创建型设计模式`。
@@ -303,6 +300,6 @@ public class AppleTechCompany extends TechFactory {
**缺点:** 添加一种新的产品的时候,需要在工厂接口及其工厂子类都添加实现,不符合开闭原则;增加了系统的抽象性和理解难度
-## 7. 完整代码地址
+## 7. 完整代码地址
-https://github.com/xkcoding/design-pattern/tree/master/src/main/java/com/xkcoding/design/pattern/creational/abstractfactory
\ No newline at end of file
+https://github.com/xkcoding/design-pattern/tree/master/src/main/java/com/xkcoding/design/pattern/creational/abstractfactory
diff --git a/source/_posts/2019-08-12.design-pattern-singleton.md b/src/data/blog/2019-08-12.design-pattern-singleton.md
similarity index 96%
rename from source/_posts/2019-08-12.design-pattern-singleton.md
rename to src/data/blog/2019-08-12.design-pattern-singleton.md
index 7f8bb464..f6e11463 100644
--- a/source/_posts/2019-08-12.design-pattern-singleton.md
+++ b/src/data/blog/2019-08-12.design-pattern-singleton.md
@@ -1,18 +1,15 @@
---
-title: 设计模式之创建型设计模式-单例模式
-comments: true
-fancybox: false
-toc: true
-date: 2019-08-12 22:43:39
+title: "设计模式之创建型设计模式-单例模式"
+author: xkcoding
+pubDatetime: 2019-08-12T14:43:39.000Z
+slug: design-pattern-singleton
+featured: false
+draft: false
tags:
-- 设计模式
-categories:
-- 设计模式
-- 创建型设计模式
-description:
-permalink: design-pattern-singleton
-top:
+ - 设计模式
+description: "单例模式怎么写才安全?7 种实现方式全解析!饿汉式、懒汉式、双重检查锁、静态内部类、枚举、容器式、ThreadLocal——哪种最推荐?更关键的是:反序列化和反射都能破坏单例!本文深入 ObjectInputStream 源码,揭秘 readResolve 方法的原理。"
---
+
## 1. 模式简介
确保一个类在任何情况下都`绝对只有一个`实例,并提供一个全局的访问入口点;隐藏所有的构造方法;属于`创建型设计模式`。
@@ -506,7 +503,7 @@ if (obj != null && handles.lookupException(passHandle) == null && desc.hasReadRe
}
}
// 这里存在一个 if 的条件判断,desc.hasReadResolveMethod()
-// 满足条件的时候,Object rep = desc.invokeReadResolve(obj);
+// 满足条件的时候,Object rep = desc.invokeReadResolve(obj);
// 如果 rep != obj 的时候呢,就把 rep 赋值给 obj, handles.setObject(passHandle, obj = rep);
// 6. 看到这里,我们就得出一个结论:
@@ -531,7 +528,7 @@ private Method readResolveMethod;
// 省略部分代码
readResolveMethod = getInheritableMethod(cl, "readResolve", null, Object.class);
-// 再看 java.io.ObjectStreamClass#getInheritableMethod(Class> cl, String name, Class>[] argTypes, Class> returnType)
+// 再看 java.io.ObjectStreamClass#getInheritableMethod(Class> cl, String name, Class>[] argTypes, Class> returnType)
// readResolveMethod 就是 函数名为 readResolve,无参,返回值是 Object 的方法
// 7.2. 再看看 desc.invokeReadResolve(obj) 这个方法 -> java.io.ObjectStreamClass#invokeReadResolve
@@ -626,4 +623,4 @@ public class LazySingletonInnerClass {
https://github.com/xkcoding/design-pattern/tree/master/src/main/java/com/xkcoding/design/pattern/creational/singleton
-测试代码里 `ConcurrentExecutor` 具体实现请看这里:https://github.com/xkcoding/design-pattern/blob/master/src/main/java/com/xkcoding/design/pattern/utils/ConcurrentExecutor.java
\ No newline at end of file
+测试代码里 `ConcurrentExecutor` 具体实现请看这里:https://github.com/xkcoding/design-pattern/blob/master/src/main/java/com/xkcoding/design/pattern/utils/ConcurrentExecutor.java
diff --git a/source/_posts/2019-08-14.design-pattern-prototype.md b/src/data/blog/2019-08-14.design-pattern-prototype.md
similarity index 91%
rename from source/_posts/2019-08-14.design-pattern-prototype.md
rename to src/data/blog/2019-08-14.design-pattern-prototype.md
index a6761eeb..5d1349e5 100644
--- a/source/_posts/2019-08-14.design-pattern-prototype.md
+++ b/src/data/blog/2019-08-14.design-pattern-prototype.md
@@ -1,18 +1,15 @@
---
-title: 设计模式之创建型设计模式-原型模式
-comments: true
-fancybox: false
-toc: true
-date: 2019-08-14 17:56:14
+title: "设计模式之创建型设计模式-原型模式"
+author: xkcoding
+pubDatetime: 2019-08-14T09:56:14.000Z
+slug: design-pattern-prototype
+featured: false
+draft: false
tags:
-- 设计模式
-categories:
-- 设计模式
-- 创建型设计模式
-description:
-permalink: design-pattern-prototype
-top:
+ - 设计模式
+description: "clone() 方法你真的会用吗?浅克隆的坑你踩过没?引用对象地址相同,改一个全变了!本文用「印钞机打印纸币」的场景,对比浅克隆 vs 深克隆的实现差异。深克隆需要实现 Serializable 接口,通过序列化/反序列化实现真正的对象复制。附 BeanUtils、JSON.parseObject 等常见应用。"
---
+
## 1. 模式简介
原型实例指定创建对象的种类,通过`克隆`这些原型创建新的对象。调用者不需要指定对象的创建细节,不通过调用构造函数创建对象。属于`创建型设计模式`。
@@ -263,8 +260,6 @@ money2 -> 引用对象地址:com.xkcoding.design.pattern.creational.prototype.
-
-
## 3. 应用
```java
@@ -295,4 +290,3 @@ money2 -> 引用对象地址:com.xkcoding.design.pattern.creational.prototype.
## 7. 完整代码地址
https://github.com/xkcoding/design-pattern/tree/master/src/main/java/com/xkcoding/design/pattern/creational/prototype
-
diff --git a/source/_posts/2019-08-20.design-pattern-proxy.md b/src/data/blog/2019-08-20.design-pattern-proxy.md
similarity index 98%
rename from source/_posts/2019-08-20.design-pattern-proxy.md
rename to src/data/blog/2019-08-20.design-pattern-proxy.md
index 1d13f731..34abad64 100644
--- a/source/_posts/2019-08-20.design-pattern-proxy.md
+++ b/src/data/blog/2019-08-20.design-pattern-proxy.md
@@ -1,17 +1,13 @@
---
-title: 设计模式之结构型设计模式-代理模式
-comments: true
-fancybox: false
-toc: true
-date: 2019-08-20 18:24:49
+title: "设计模式之结构型设计模式-代理模式"
+author: xkcoding
+pubDatetime: 2019-08-20T10:24:49.000Z
+slug: design-pattern-proxy
+featured: false
+draft: false
tags:
-- 设计模式
-categories:
-- 设计模式
-- 结构型设计模式
-description:
-permalink: design-pattern-proxy
-top:
+ - 设计模式
+description: "Spring AOP 底层到底用的啥?JDK 动态代理 vs CGLIB 动态代理,区别在哪?本文用「售票员卖票」场景讲解:静态代理只能代理特定类,动态代理可代理任意对象。附 JDK Proxy 源码深度剖析(从 newProxyInstance 到 ProxyClassFactory),以及 Spring 的代理选择原则。"
---
## 1. 模式简介
@@ -448,7 +444,7 @@ public class PatternTest {
```java
// 1. musicTicket 是通过我们定义的 getInstance 方法返回的,在 getInstance 方法里,我们通过调用 Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this)
-// 2. 我们看看 java.lang.reflect.Proxy#newProxyInstance(ClassLoader loader,Class>[] interfaces,InvocationHandler h)
+// 2. 我们看看 java.lang.reflect.Proxy#newProxyInstance(ClassLoader loader,Class>[] interfaces,InvocationHandler h)
// java.lang.reflect.Proxy#newProxyInstance 主要工作就是①生成代理类②根据代理类获取构造方法③通过构造方法生成代理对象
// ①生成代理类 -> Class> cl = getProxyClass0(loader, intfs);
// ②根据代理类获取构造方法 -> final Constructor> cons = cl.getConstructor(constructorParams);
@@ -596,7 +592,7 @@ private static final class KeyFactory
// 8. 然后我们到了关键的地方,敲黑板,划重点了!!!我们来看下二级缓存值的获取,在WeakCache中是通过 supplier.get() 获取的,那么关键就是这个 supplier 了。
// 还记得上面说过的,V value = supplier.get() 取到的可能是 WeakCache.Factory 也可能是 CacheValue
-// 那我们看看 WeakCache.Factory
+// 那我们看看 WeakCache.Factory
/**
* A factory {@link Supplier} that implements the lazy synchronized
@@ -1150,4 +1146,4 @@ System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/Users/yangkai
## 7. 完整代码地址
-https://github.com/xkcoding/design-pattern/tree/master/src/main/java/com/xkcoding/design/pattern/structural/proxy
\ No newline at end of file
+https://github.com/xkcoding/design-pattern/tree/master/src/main/java/com/xkcoding/design/pattern/structural/proxy
diff --git a/source/_posts/2019-08-27.design-pattern-delegate.md b/src/data/blog/2019-08-27.design-pattern-delegate.md
similarity index 88%
rename from source/_posts/2019-08-27.design-pattern-delegate.md
rename to src/data/blog/2019-08-27.design-pattern-delegate.md
index 8cbb3db0..58c1cf77 100644
--- a/source/_posts/2019-08-27.design-pattern-delegate.md
+++ b/src/data/blog/2019-08-27.design-pattern-delegate.md
@@ -1,18 +1,15 @@
---
-title: 设计模式之行为型设计模式-委派模式
-comments: true
-fancybox: false
-toc: true
-date: 2019-08-27 16:45:41
+title: "设计模式之行为型设计模式-委派模式"
+author: xkcoding
+pubDatetime: 2019-08-27T08:45:41.000Z
+slug: design-pattern-delegate
+featured: false
+draft: false
tags:
-- 设计模式
-categories:
-- 设计模式
-- 行为型设计模式
-description:
-permalink: design-pattern-delegate
-top:
+ - 设计模式
+description: "委派模式和代理模式傻傻分不清?核心区别:代理注重过程,委派注重结果!本文用「老板-组长-工人」的场景讲解:Boss 下命令,Leader 派发任务,Worker 执行。虽然不属于 GoF 23 种设计模式,但 Spring MVC 的 DispatcherServlet 就是典型应用。"
---
+
## 1. 模式简介
负责任务的调度和分配任务,跟代理模式很像,可以看做是一种特殊情况下的静态代理的全权代理,但是`代理模式注重过程`,而`委派模式注重结果`;属于`行为型设计模式`;`不属于23种设计模式`。
@@ -172,11 +169,11 @@ public class PatternTest {
我是领导,我现在需要处理: cooking
我是组长,领导需要: cooking,我来安排任务: cooking
我是厨师,我接到任务,需要做美食。
--------------------
+-------------------
我是领导,我现在需要处理: sewing
我是组长,领导需要: sewing,我来安排任务: sewing
我是缝纫工,我接到任务,需要做衣服。
--------------------
+-------------------
我是领导,我现在需要处理: selling
我是组长,领导需要: selling,我来安排任务: selling
目前办公室没有对应职位的工人
@@ -204,4 +201,3 @@ public class PatternTest {
## 6. 完整代码地址
https://github.com/xkcoding/design-pattern/tree/master/src/main/java/com/xkcoding/design/pattern/behavioral/delegate
-
diff --git a/source/_posts/2019-08-29.early-experience-about-github-actions.md b/src/data/blog/2019-08-29.early-experience-about-github-actions.md
similarity index 80%
rename from source/_posts/2019-08-29.early-experience-about-github-actions.md
rename to src/data/blog/2019-08-29.early-experience-about-github-actions.md
index c5145f0a..c46c5f45 100644
--- a/source/_posts/2019-08-29.early-experience-about-github-actions.md
+++ b/src/data/blog/2019-08-29.early-experience-about-github-actions.md
@@ -1,20 +1,16 @@
---
-title: GitHub Actions 初体验
-comments: true
-fancybox: false
-toc: true
-date: 2019-08-29 14:54:05
+title: "GitHub Actions 初体验"
+author: xkcoding
+pubDatetime: 2019-08-29T06:54:05.000Z
+slug: early-experience-about-github-actions
+featured: false
+draft: false
tags:
-- devops
-- ci
-- GitHub
-categories:
-- 技术相关
-- devops
-description:
-permalink: early-experience-about-github-actions
-top:
+ - devops
+ - github
+description: "还在用 Jenkins、Travis-CI?GitHub 自家的 CI/CD 来了!GitHub Actions 支持自定义工作流,开源项目免费使用。本文记录申请流程和 Spring Boot + Maven 项目的配置示例,push 代码自动触发构建,真香!附完整 maven.yml 配置。"
---
+
## 前言
前段时间同性交友网站 `GitHub` 推出了自家的 `Registry` 服务,支持多种语言的镜像服务,具体参见:https://help.github.com/en/articles/about-github-package-registry
@@ -69,8 +65,6 @@ top:

-
-
## 3. 配置语法
具体的配置语法,还是参考官方文档:https://help.github.com/en/articles/workflow-syntax-for-github-actions
@@ -90,15 +84,15 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v1
- - name: Set up JDK 1.8
- uses: actions/setup-java@v1
- with:
- java-version: 1.8
- - name: Build with Maven
- run: mvn clean package -DskipTests=true -Dmaven.javadoc.skip=true -B -V
+ - uses: actions/checkout@v1
+ - name: Set up JDK 1.8
+ uses: actions/setup-java@v1
+ with:
+ java-version: 1.8
+ - name: Build with Maven
+ run: mvn clean package -DskipTests=true -Dmaven.javadoc.skip=true -B -V
```
## 5. 参考
-- 官方文档:https://help.github.com/en/articles/workflow-syntax-for-github-actions
\ No newline at end of file
+- 官方文档:https://help.github.com/en/articles/workflow-syntax-for-github-actions
diff --git a/source/_posts/2019-09-03.design-pattern-strategy.md b/src/data/blog/2019-09-03.design-pattern-strategy.md
similarity index 91%
rename from source/_posts/2019-09-03.design-pattern-strategy.md
rename to src/data/blog/2019-09-03.design-pattern-strategy.md
index e05df778..17018e5a 100644
--- a/source/_posts/2019-09-03.design-pattern-strategy.md
+++ b/src/data/blog/2019-09-03.design-pattern-strategy.md
@@ -1,18 +1,15 @@
---
-title: 设计模式之行为型设计模式-策略模式
-comments: true
-fancybox: false
-toc: true
-date: 2019-09-03 18:01:29
+title: "设计模式之行为型设计模式-策略模式"
+author: xkcoding
+pubDatetime: 2019-09-03T10:01:29.000Z
+slug: design-pattern-strategy
+featured: false
+draft: false
tags:
-- 设计模式
-categories:
-- 设计模式
-- 行为型设计模式
-description:
-permalink: design-pattern-strategy
-top:
+ - 设计模式
+description: "支付宝、微信、京东、银联……一堆 if-else 判断支付方式?策略模式帮你优雅消灭!本文用「选择支付方式」的场景讲解:定义 Payment 接口,不同支付方式各自实现。JDK 的 Comparator、Spring 的 Resource 都是典型应用。符合开闭原则,扩展新支付只需新增类。"
---
+
## 1. 模式简介
定义了一系列算法、分别封装起来,让它们之间可以互相替换,此模式让算法的变化不会影响到使用算法的用户。可以避免多重分支的 `if...else...` 和 `switch` 语句。属于`行为型设计模式`。
@@ -337,7 +334,7 @@ public class PatternTest {
// java.util.Arrays#parallelSort(int[], int, int)
// java.util.TreeMap#TreeMap(java.util.Comparator super K>)
-// Spring 的 Resource 接口,比如:
+// Spring 的 Resource 接口,比如:
// ClassPathResource
// UrlResource
// FileUrlResource
@@ -349,7 +346,7 @@ public class PatternTest {
## 5. 场景
-- 假如系统中有很多类,而他们的区别仅仅在于他们的行为不同。
+- 假如系统中有很多类,而他们的区别仅仅在于他们的行为不同。
- 一个系统需要动态地在几种算法中选择一种。
## 6. 优缺点
@@ -361,4 +358,3 @@ public class PatternTest {
## 7. 完整代码地址
https://github.com/xkcoding/design-pattern/tree/master/src/main/java/com/xkcoding/design/pattern/behavioral/strategy
-
diff --git a/source/_posts/2019-09-13.design-pattern-adapter.md b/src/data/blog/2019-09-13.design-pattern-adapter.md
similarity index 87%
rename from source/_posts/2019-09-13.design-pattern-adapter.md
rename to src/data/blog/2019-09-13.design-pattern-adapter.md
index 8bbdbf67..0163762c 100644
--- a/source/_posts/2019-09-13.design-pattern-adapter.md
+++ b/src/data/blog/2019-09-13.design-pattern-adapter.md
@@ -1,18 +1,15 @@
---
-title: 设计模式之结构型设计模式-适配器模式
-comments: true
-fancybox: false
-toc: true
-date: 2019-09-13 16:18:23
+title: "设计模式之结构型设计模式-适配器模式"
+author: xkcoding
+pubDatetime: 2019-09-13T08:18:23.000Z
+slug: design-pattern-adapter
+featured: false
+draft: false
tags:
-- 设计模式
-categories:
-- 设计模式
-- 结构型设计模式
-description:
-permalink: design-pattern-adapter
-top:
+ - 设计模式
+description: "电脑 USB 接口和手机 Type-C 不兼容?买个转接头啊!适配器模式就是代码世界的「转接头」。本文用 USB 转 Type-C 的场景讲解:老接口不改动,新增适配器类做转换。Spring MVC 的 HandlerAdapter、Spring AOP 的 AdvisorAdapter 都是典型应用。"
---
+
## 1. 模式简介
将一个原有的类的接口转换成期望的另一个接口,使原本的接口不兼容的类可以一起工作。属于`结构型设计模式`。
@@ -139,7 +136,6 @@ public class PatternTest {
-
## 4. 应用
```java
@@ -163,4 +159,3 @@ public class PatternTest {
## 7. 完整代码地址
https://github.com/xkcoding/design-pattern/tree/master/src/main/java/com/xkcoding/design/pattern/structural/adapter
-
diff --git a/source/_posts/2019-10-08.docker-easy-mock.md b/src/data/blog/2019-10-08.docker-easy-mock.md
similarity index 87%
rename from source/_posts/2019-10-08.docker-easy-mock.md
rename to src/data/blog/2019-10-08.docker-easy-mock.md
index c2c962ca..a4615bab 100644
--- a/source/_posts/2019-10-08.docker-easy-mock.md
+++ b/src/data/blog/2019-10-08.docker-easy-mock.md
@@ -1,19 +1,16 @@
---
-title: 使用 docker 运行 easy-mock
-comments: true
-fancybox: false
-toc: true
-date: 2019-10-08 09:49:36
+title: "使用 docker 运行 easy-mock"
+author: xkcoding
+pubDatetime: 2019-10-08T01:49:36.000Z
+slug: docker-easy-mock
+featured: false
+draft: false
tags:
-- docker
-- easy mock
-categories:
-- 技术相关
-- docker
-description:
-permalink: docker-easy-mock
-top:
+ - docker
+ - easy-mock
+description: "前后端并行开发,接口还没写好怎么办?Easy Mock 帮你搞定 Mock 数据!本文使用 docker-compose 一键部署 Easy Mock(含 MongoDB + Redis),附 nginx 配置 HTTPS 反向代理。告别手写假数据、拦截请求的麻烦,可视化创建接口,支持 Swagger 导入。"
---
+
## 前言
有时候,我们写前端页面或者小程序的时候,如果有数据,我们就可以更好的进行开发,但是实际场景中,前后端是并行开发的,并没有现成的后端接口给我们用,这时候 mock 假数据的作用有显得十分有必要了。
@@ -36,7 +33,7 @@ top:
## 2. 什么是 Easy Mock
-Easy Mock 是一个可视化,并且能快速生成 **模拟数据** 的持久化服务。
+Easy Mock 是一个可视化,并且能快速生成 **模拟数据** 的持久化服务。
官方网站:https://www.easy-mock.com/
@@ -64,7 +61,7 @@ Easy Mock 是一个可视化,并且能快速生成 **模拟数据** 的持久
> 官方文档:https://docs.docker.com/compose/install/
-1. 首先你得确定拥有 docker 环境,*如果你是 Windows / Mac 用户,那么安装客户端,就会自带 docker-compose 了*。
+1. 首先你得确定拥有 docker 环境,_如果你是 Windows / Mac 用户,那么安装客户端,就会自带 docker-compose 了_。
2. 因为本次我们是在服务器搭建,当前服务器系统是 CentOS 7,所以我们需要自行安装 docker-compose。
@@ -83,7 +80,7 @@ Easy Mock 是一个可视化,并且能快速生成 **模拟数据** 的持久
5. 验证是否安装成功
```bash
- $ docker-compose version
+ $ docker-compose version
docker-compose version 1.24.1, build 4667896b
docker-py version: 3.7.3
CPython version: 3.6.8
@@ -95,14 +92,14 @@ Easy Mock 是一个可视化,并且能快速生成 **模拟数据** 的持久
> 注意看文件中的注释信息,有一处 npm start 修改为 npm run dev,是因为我看了下 easy-mock 的源码 package.json 中 npm start 命令跑的是 production 模式,需要额外配置,为了简单起见,我们使用 npm run dev 直接运行 dev 模式
```yaml
-version: '3'
+version: "3"
services:
mongodb:
image: mongo:3.4.1
volumes:
# /apps/easy-mock/data/db 是数据库文件存放地址,根据需要修改为本地地址
- - '/apps/easy-mock/data/db:/data/db'
+ - "/apps/easy-mock/data/db:/data/db"
networks:
- easy-mock
restart: always
@@ -112,7 +109,7 @@ services:
command: redis-server --appendonly yes
volumes:
# /apps/easy-mock/data/redis 是 redis 数据文件存放地址,根据需要修改为本地地址
- - '/apps/easy-mock/data/redis:/data'
+ - "/apps/easy-mock/data/redis:/data"
networks:
- easy-mock
restart: always
@@ -125,7 +122,7 @@ services:
- 7300:7300
volumes:
# 日志地址,根据需要修改为本地地址
- - '/apps/easy-mock/logs:/home/easy-mock/easy-mock/logs'
+ - "/apps/easy-mock/logs:/home/easy-mock/easy-mock/logs"
networks:
- easy-mock
restart: always
@@ -150,7 +147,7 @@ $ docker-compose up -d
server {
listen 443;
server_name mock.xkcoding.com; # 监听的域名
-
+
client_max_body_size 5G; # 突破上传大文件限制
ssl on;
ssl_certificate xxxxxx.pem; # https 的 pem 文件
@@ -197,4 +194,4 @@ $ nginx -s reload
## 6. 参考
- [easy mock docker 官方仓库](https://github.com/easy-mock/easy-mock-docker)
-- [easy mock 官方仓库](https://github.com/easy-mock/easy-mock)
\ No newline at end of file
+- [easy mock 官方仓库](https://github.com/easy-mock/easy-mock)
diff --git a/source/_posts/2019-10-14.easy-mock-syntax.md b/src/data/blog/2019-10-14.easy-mock-syntax.md
similarity index 61%
rename from source/_posts/2019-10-14.easy-mock-syntax.md
rename to src/data/blog/2019-10-14.easy-mock-syntax.md
index 1bbb0215..a77dd883 100644
--- a/source/_posts/2019-10-14.easy-mock-syntax.md
+++ b/src/data/blog/2019-10-14.easy-mock-syntax.md
@@ -1,17 +1,15 @@
---
-title: Easy Mock 基本用法
-comments: true
-fancybox: false
-toc: true
-date: 2019-10-14 18:10:48
+title: "Easy Mock 基本用法"
+author: xkcoding
+pubDatetime: 2019-10-14T10:10:48.000Z
+slug: easy-mock-syntax
+featured: false
+draft: false
tags:
-- easy mock
-categories:
-- 技术相关
-description:
-permalink: easy-mock-syntax
-top:
+ - easy-mock
+description: "Mock 数据不够真实?Easy Mock + Mock.js 让假数据活起来!本文详解:基础语法生成随机数据、数据占位符模拟真实场景、响应式数据根据请求参数返回。重点:如何用 Easy Mock 实现分页数据接口,附完整模板代码。前端小姐姐再也不用求后端了!"
---
+
## 前言
在上一篇文章,我们了解了如何[《使用 docker 运行 easy-mock》](https://xkcoding.com/2019/10/08/docker-easy-mock.html),这篇文章我们来学习下 Easy Mock 的基本用法,掌握 Easy Mock 的奇淫技巧,前端小姐姐写页面再也不需要去求后端的小哥哥了。
@@ -22,8 +20,7 @@ top:
> Easy Mock 集成了 `Mock.js` ,因此可以使用 `Mock.js` 的语法来 mock 数据。
>
-> 1.支持生成随机的文本、数字、布尔值、日期、邮箱、链接、图片、颜色等;
-> 2.支持扩展更多数据类型,支持自定义函数和正则。
+> 1.支持生成随机的文本、数字、布尔值、日期、邮箱、链接、图片、颜色等;2.支持扩展更多数据类型,支持自定义函数和正则。
定义返回格式如下:
@@ -125,16 +122,7 @@ top:
"title": "Unrhbf Xrlpkb Yxzd",
"cparagraph": "老书称明选京基属处观取容形观角清行。给克群阶外活经导治书华与十下者志以导。派传当才已劳支南学示期构该。",
"csentence": "传使引都值四六始常同进放传转指每。",
- "range": [
- 2,
- 3,
- 4,
- 5,
- 6,
- 7,
- 8,
- 9
- ]
+ "range": [2, 3, 4, 5, 6, 7, 8, 9]
}
```
@@ -166,9 +154,9 @@ https://mock.xkcoding.com/mock/5dce0fdaaac115001c03b239/test/test3/1
```json
{
- "data": {
- "id": "1"
- }
+ "data": {
+ "id": "1"
+ }
}
```
@@ -202,10 +190,10 @@ https://mock.xkcoding.com/mock/5dce0fdaaac115001c03b239/test/test4
```json
{
- "data": {
- "id": "name 为空"
- },
- "success": false
+ "data": {
+ "id": "name 为空"
+ },
+ "success": false
}
```
@@ -217,10 +205,10 @@ https://mock.xkcoding.com/mock/5dce0fdaaac115001c03b239/test/test4?name=xkcoding
```json
{
- "data": {
- "id": "xkcoding"
- },
- "success": true
+ "data": {
+ "id": "xkcoding"
+ },
+ "success": true
}
```
@@ -246,11 +234,8 @@ https://mock.xkcoding.com/mock/5dce0fdaaac115001c03b239/test/test5
```json
{
- "text": "测试文本",
- "likes": [
- "LOL",
- "CODE"
- ]
+ "text": "测试文本",
+ "likes": ["LOL", "CODE"]
}
```
@@ -258,13 +243,10 @@ https://mock.xkcoding.com/mock/5dce0fdaaac115001c03b239/test/test5
```json
{
- "data": {
- "text": "测试文本",
- "likes": [
- "LOL",
- "CODE"
- ]
- }
+ "data": {
+ "text": "测试文本",
+ "likes": ["LOL", "CODE"]
+ }
}
```
@@ -359,36 +341,36 @@ https://mock.xkcoding.com/mock/5dce0fdaaac115001c03b239/test/test6?currentPage=1
```json
{
- "code": 200,
- "success": true,
- "msg": "操作成功",
- "data": {
- "total": 94,
- "list": [
- {
- "id": "420000198912064299",
- "title": "《东路运院少派成开团》",
- "summary": "处前类",
- "image": "http://dummyimage.com/240x16:9/79f2ee/FFF.jpg&text=xkcoding",
- "publishTime": "2019-11-15 07:31:44",
- "author": "xkcoding",
- "watch": 990,
- "star": 35,
- "fork": 63
- },
- {
- "id": "22000019910311824X",
- "title": "《气影几把理时界海》",
- "summary": "志定业",
- "image": "http://dummyimage.com/256x16:9/f2cb79/FFF.jpg&text=xkcoding",
- "publishTime": "2019-11-15 07:31:44",
- "author": "xkcoding",
- "watch": 413,
- "star": 29,
- "fork": 51
- }
- ]
- }
+ "code": 200,
+ "success": true,
+ "msg": "操作成功",
+ "data": {
+ "total": 94,
+ "list": [
+ {
+ "id": "420000198912064299",
+ "title": "《东路运院少派成开团》",
+ "summary": "处前类",
+ "image": "http://dummyimage.com/240x16:9/79f2ee/FFF.jpg&text=xkcoding",
+ "publishTime": "2019-11-15 07:31:44",
+ "author": "xkcoding",
+ "watch": 990,
+ "star": 35,
+ "fork": 63
+ },
+ {
+ "id": "22000019910311824X",
+ "title": "《气影几把理时界海》",
+ "summary": "志定业",
+ "image": "http://dummyimage.com/256x16:9/f2cb79/FFF.jpg&text=xkcoding",
+ "publishTime": "2019-11-15 07:31:44",
+ "author": "xkcoding",
+ "watch": 413,
+ "star": 29,
+ "fork": 51
+ }
+ ]
+ }
}
```
@@ -396,37 +378,37 @@ https://mock.xkcoding.com/mock/5dce0fdaaac115001c03b239/test/test6?currentPage=1
Easy Mock 响应式数据中,可以为某个属性指定 `Function`,为 Function 提供了 `_req` 对象,下面是 Function 的参数说明:
-| 对象 | 描述 |
-| :------------------ | :----------------------------------------------------------- |
-| Mock | Mock 对象 |
-| _req.url | 获得请求 url 地址 |
-| _req.method | 获取请求方法 |
-| _req.params | 获取 url 参数对象 |
-| _req.querystring | 获取查询参数字符串(url中?后面的部分),不包含 ? |
-| _req.query | 将查询参数字符串进行解析并以对象的形式返回,如果没有查询参数字符串则返回一个空对象 |
-| _req.body | 当 post 请求以 `x-www-form-urlencoded` 方式提交时,我们可以拿到请求的参数对象 |
-| _req.path | 获取请求路径名 |
-| _req.header | 获取请求头对象 |
-| _req.originalUrl | 获取请求原始地址 |
-| _req.search | 获取查询参数字符串,包含 ? |
-| _req.host | 获取 host (hostname:port) |
-| _req.hostname | 获取 hostname |
-| _req.type | 获取请求 Content-Type,不包含像 "charset" 这样的参数 |
-| _req.protocol | 返回请求协议 |
-| _req.ip | 请求远程地址 |
-| _req.get(field) | 获取请求 header 中对应 field 的值 |
-| _req.cookies(field) | 获取请求 cookies 中对应 field 的值 |
+| 对象 | 描述 |
+| :------------------- | :--------------------------------------------------------------------------------- |
+| Mock | Mock 对象 |
+| \_req.url | 获得请求 url 地址 |
+| \_req.method | 获取请求方法 |
+| \_req.params | 获取 url 参数对象 |
+| \_req.querystring | 获取查询参数字符串(url中?后面的部分),不包含 ? |
+| \_req.query | 将查询参数字符串进行解析并以对象的形式返回,如果没有查询参数字符串则返回一个空对象 |
+| \_req.body | 当 post 请求以 `x-www-form-urlencoded` 方式提交时,我们可以拿到请求的参数对象 |
+| \_req.path | 获取请求路径名 |
+| \_req.header | 获取请求头对象 |
+| \_req.originalUrl | 获取请求原始地址 |
+| \_req.search | 获取查询参数字符串,包含 ? |
+| \_req.host | 获取 host (hostname:port) |
+| \_req.hostname | 获取 hostname |
+| \_req.type | 获取请求 Content-Type,不包含像 "charset" 这样的参数 |
+| \_req.protocol | 返回请求协议 |
+| \_req.ip | 请求远程地址 |
+| \_req.get(field) | 获取请求 header 中对应 field 的值 |
+| \_req.cookies(field) | 获取请求 cookies 中对应 field 的值 |
同时也提供了自定义响应的配置,封装在 `_res` 对象中
-| 字段 | 描述 |
-| :------ | :----------------------------------------------------------- |
-| status | 可以定制返回的 http status code,默认是 200 |
-| cookies | 可以定制需要设置的 cookie(暂时不支持设置过期时间等) |
-| headers | 可以定制返回的 response 的 header |
-| data | 如果有这个字段,会以此数据直接覆盖整个返回的数据,并且此处不支持 mock 的语法(如果 _res.status 的值为 200,则不会覆盖默认定义的 mock 数据) |
+| 字段 | 描述 |
+| :------ | :------------------------------------------------------------------------------------------------------------------------------------------- |
+| status | 可以定制返回的 http status code,默认是 200 |
+| cookies | 可以定制需要设置的 cookie(暂时不支持设置过期时间等) |
+| headers | 可以定制返回的 response 的 header |
+| data | 如果有这个字段,会以此数据直接覆盖整个返回的数据,并且此处不支持 mock 的语法(如果 \_res.status 的值为 200,则不会覆盖默认定义的 mock 数据) |
## 6. 参考
- [Easy Mock 官方文档](https://easy-mock.com/docs)
-- [Mock.js 官方文档](http://mockjs.com/)
\ No newline at end of file
+- [Mock.js 官方文档](http://mockjs.com/)
diff --git a/source/_posts/2019-10-16.design-pattern-template.md b/src/data/blog/2019-10-16.design-pattern-template.md
similarity index 89%
rename from source/_posts/2019-10-16.design-pattern-template.md
rename to src/data/blog/2019-10-16.design-pattern-template.md
index 9888dfe6..dc1ef231 100644
--- a/source/_posts/2019-10-16.design-pattern-template.md
+++ b/src/data/blog/2019-10-16.design-pattern-template.md
@@ -1,18 +1,15 @@
---
-title: 设计模式之行为型设计模式-模板方法模式
-comments: true
-fancybox: false
-toc: true
-date: 2019-10-16 16:58:52
+title: "设计模式之行为型设计模式-模板方法模式"
+author: xkcoding
+pubDatetime: 2019-10-16T08:58:52.000Z
+slug: design-pattern-template
+featured: false
+draft: false
tags:
-- 设计模式
-categories:
-- 设计模式
-- 行为型设计模式
-description:
-permalink: design-pattern-template
-top:
+ - 设计模式
+description: "JdbcTemplate、RedisTemplate、MongoTemplate……Spring 为啥这么爱用 Template?因为模板方法模式太香了!本文用「短信/邮件发送器」的场景讲解:父类定义 validate → execute → error 的流程骨架,子类只需实现具体步骤。公共逻辑不重复,扩展新渠道只加子类。"
---
+
## 1. 模式简介
定义一个程序的整体流程,并允许子类为其中的一个或者多个步骤提供具体实现。模板方法使得子类可以在不改变整体流程的情况下,重新定义其中的某些步骤。属于`行为型设计模式`。
@@ -234,4 +231,4 @@ public class PatternTest {
## 7. 完整代码地址
-https://github.com/xkcoding/design-pattern/tree/master/src/main/java/com/xkcoding/design/pattern/behavioral/template
\ No newline at end of file
+https://github.com/xkcoding/design-pattern/tree/master/src/main/java/com/xkcoding/design/pattern/behavioral/template
diff --git a/source/_posts/2019-10-25.design-pattern-observer.md b/src/data/blog/2019-10-25.design-pattern-observer.md
similarity index 87%
rename from source/_posts/2019-10-25.design-pattern-observer.md
rename to src/data/blog/2019-10-25.design-pattern-observer.md
index 04e32ac8..78dfbfcd 100644
--- a/source/_posts/2019-10-25.design-pattern-observer.md
+++ b/src/data/blog/2019-10-25.design-pattern-observer.md
@@ -1,18 +1,15 @@
---
-title: 设计模式之行为型设计模式-观察者模式
-comments: true
-fancybox: false
-toc: true
-date: 2019-10-25 17:02:55
+title: "设计模式之行为型设计模式-观察者模式"
+author: xkcoding
+pubDatetime: 2019-10-25T09:02:55.000Z
+slug: design-pattern-observer
+featured: false
+draft: false
tags:
-- 设计模式
-categories:
-- 设计模式
-- 行为型设计模式
-description:
-permalink: design-pattern-observer
-top:
+ - 设计模式
+description: "微信朋友圈点赞,为什么所有好友都能收到通知?因为观察者模式!本文用「私信推送通知」的场景讲解:Pusher 作为被观察者发布消息,User 作为观察者订阅并接收。setChanged() + notifyObservers() 触发通知。JDK Observer、Guava EventBus、Spring ApplicationEvent 都是典型应用。"
---
+
## 1. 模式简介
定义了对象之间的一对多依赖,让多个观察者对象同时监听一个主体对象,当主体对象发生变化时,它的所有观察者对象都会收到通知并更新。属于`行为型设计模式`。观察者模式也被叫做`发布订阅模式`。
@@ -178,4 +175,3 @@ xkcoding,你收到一条来自 user-1 的私信!
## 7. 完整代码地址
https://github.com/xkcoding/design-pattern/tree/master/src/main/java/com/xkcoding/design/pattern/behavioral/observer
-
diff --git a/source/_posts/2019-11-11.design-pattern-decorator.md b/src/data/blog/2019-11-11.design-pattern-decorator.md
similarity index 92%
rename from source/_posts/2019-11-11.design-pattern-decorator.md
rename to src/data/blog/2019-11-11.design-pattern-decorator.md
index 907a2445..4936c854 100644
--- a/source/_posts/2019-11-11.design-pattern-decorator.md
+++ b/src/data/blog/2019-11-11.design-pattern-decorator.md
@@ -1,23 +1,19 @@
---
-title: 设计模式之结构型设计模式-装饰者模式
-comments: true
-fancybox: false
-toc: true
-date: 2019-11-11 17:32:01
+title: "设计模式之结构型设计模式-装饰者模式"
+author: xkcoding
+pubDatetime: 2019-11-11T09:32:01.000Z
+slug: design-pattern-decorator
+featured: false
+draft: false
tags:
-- 设计模式
-categories:
-- 设计模式
-- 结构型设计模式
-description:
-permalink: design-pattern-decorator
-top:
+ - 设计模式
+description: "BufferedReader、InputStream、OutputStream……JDK IO 类为什么可以层层包装?装饰者模式!本文用「购买 MacBook Pro 升级内存/硬盘套餐」的场景讲解:基础套餐 → 叠加内存装饰器 → 叠加硬盘装饰器,动态扩展功能。比继承更灵活,符合开闭原则。"
---
+
## 1. 模式简介
在不改变原有对象的基础之上,将新功能附加到原有对象上,提供了比继承更有弹性的解决方案(扩展原有对象的功能),属于`结构型设计模式`。
-
## 2. 示例代码
@@ -341,7 +337,6 @@ public class PatternTest {
-
## 4. 应用
```java
@@ -367,4 +362,4 @@ public class PatternTest {
## 7. 完整代码地址
-https://github.com/xkcoding/design-pattern/tree/master/src/main/java/com/xkcoding/design/pattern/structural/decorator
\ No newline at end of file
+https://github.com/xkcoding/design-pattern/tree/master/src/main/java/com/xkcoding/design/pattern/structural/decorator
diff --git a/source/_posts/2019-12-26.xkcoding-simple-http.md b/src/data/blog/2019-12-26.xkcoding-simple-http.md
similarity index 94%
rename from source/_posts/2019-12-26.xkcoding-simple-http.md
rename to src/data/blog/2019-12-26.xkcoding-simple-http.md
index 5eb72aad..2bfc428f 100644
--- a/source/_posts/2019-12-26.xkcoding-simple-http.md
+++ b/src/data/blog/2019-12-26.xkcoding-simple-http.md
@@ -1,19 +1,16 @@
---
-title: 简单 HTTP 工具
-comments: true
-fancybox: false
-toc: true
-date: 2019-12-26 18:03:23
-tags:
-- JustAuth
-- 开源
-categories:
-- 开源
-- simple-http
-description:
-permalink: xkcoding-simple-http
-top:
+title: "简单 HTTP 工具"
+author: xkcoding
+pubDatetime: 2019-12-26T10:03:23.000Z
+slug: xkcoding-simple-http
+featured: false
+draft: false
+tags:
+ - justauth
+ - 开源
+description: "JustAuth 强依赖 hutool-http,想换成 OkHttp3 怎么办?simple-http 来解耦!设计思路:定义通用 Http 接口,提供 4 种默认实现(JDK11 HttpClient、OkHttp3、Apache HttpClient、Hutool)。通过 Class.forName() 自动探测依赖,也支持自定义实现。运用了代理模式、委派模式、策略模式。"
---
+
## 1. 前言
因为本人是 [JustAuth](https://github.com/justauth/JustAuth) 的主要贡献者之一,`JustAuth` 里需要和各大平台做 HTTP 交互来换取 Token、用户信息等数据,使用的是 `hutool-http`来实现 HTTP 请求的发送。前段时间,有朋友提出一个需求:能否使用 `OkHttp3` 等更优秀的 HTTP 请求工具来替换默认的 `hutool-http` ?于是写一个 `simple-http` 的想法就诞生了。
@@ -167,7 +164,7 @@ public class HttpUtil {
public void setHttp(Http http) {
proxy = http;
}
-
+
private void checkHttpNotNull(Http proxy) {
if (null == proxy) {
throw new SimpleHttpException("HTTP 实现类未指定!");
@@ -341,4 +338,4 @@ public class HttpUtil {
- `simple-http` 预计会在 `JustAuth` 的 `1.4.x` 版本正式上线,目前整合已完毕(2019.12.25),已提交 [PR](https://github.com/justauth/JustAuth/pull/59),待小组成员仔细测试之后就会发布。届时欢迎小伙伴们尝试~
- `simple-http` 的灵感来自 [@春哥](https://github.com/ChunMengLu) 的 jfinal-weixin 的 [HttpUtils](https://gitee.com/jfinal/jfinal-weixin/blob/master/src/main/java/com/jfinal/weixin/sdk/utils/HttpUtils.java)。
-- JDK 11 的 `HttpClient` 实现,本人菜的一逼,所以主要也是 [@春哥](https://github.com/ChunMengLu) 的贡献,膜拜就完事了~
\ No newline at end of file
+- JDK 11 的 `HttpClient` 实现,本人菜的一逼,所以主要也是 [@春哥](https://github.com/ChunMengLu) 的贡献,膜拜就完事了~
diff --git a/source/_posts/2020-01-01.bye2019-hello2020.md b/src/data/blog/2020-01-01.bye2019-hello2020.md
similarity index 91%
rename from source/_posts/2020-01-01.bye2019-hello2020.md
rename to src/data/blog/2020-01-01.bye2019-hello2020.md
index 373c7463..b6e6e025 100644
--- a/source/_posts/2020-01-01.bye2019-hello2020.md
+++ b/src/data/blog/2020-01-01.bye2019-hello2020.md
@@ -1,17 +1,15 @@
---
-title: 又是一年总结时
-comments: true
-fancybox: false
-toc: true
-date: 2020-01-01 00:00:00
+title: "又是一年总结时"
+author: xkcoding
+pubDatetime: 2019-12-31T16:00:00.000Z
+slug: bye2019-hello2020
+featured: false
+draft: false
tags:
-- 总结展望
-categories:
-- 总结展望
-description:
-permalink: bye2019-hello2020
-top:
+ - 总结展望
+description: "十年匆匆而过,从中考吊车尾到大学专业前三,从北漂初入职场到开源贡献者。2019 年的高光时刻:spring-boot-demo 登顶 GitHub 热门 Java 榜第一名!参与 JustAuth、发布 magic-starter……回顾这一年的工作、技术、开源与生活,展望 2020 新的 decade。"
---
+
We are all in the gutter, but some of us are looking at the stars.
@@ -31,7 +29,7 @@ top:
接下来就是我的大学,在「湖北文理学院」的日子了。可能是突然长大了,也可能是高考对自己的影响太大了,自尊心不断驱使着自己去学习。截止到我大三去北京工作之前,成绩始终在本专业前三,获得过「校二级奖学金」,也获得过「国家励志奖学金」。除了校级竞赛,也参加了「软件杯」本科组优秀奖、「蓝桥杯」湖北赛区三等奖、「全国物联网大赛」二等奖,还申请了「农产品供应链」的软著。
-正是凭借自己的努力,在大三的时候,我获得了班主任及院辅导员的批准,允许我离校找工作。(*`这里需要说明一下,我大学学的是「物联网工程」专业,同时我们这一届是第一届这个专业的学生,所以当时专业课已经在大二全部结束,所以我大三可以申请离校找工作,学校的时光异常珍贵,希望学弟学妹们好好珍惜!`*)2016 年 7 月 28 号,我将自己的第一站选择了「北京」,也正是因为自己的自信和能力,以及「大卫哥」的信任,我在 2016 年 8 月 2 号正式入职(*`不是实习哦`*)我的第一家公司 ——「图迹科技」。我的工作生涯,也从此开始。在图迹的一年半时间,我遇到了好领导、好同事,感谢他们对我初入职场的照顾与包容,感恩🙏
+正是凭借自己的努力,在大三的时候,我获得了班主任及院辅导员的批准,允许我离校找工作。(_`这里需要说明一下,我大学学的是「物联网工程」专业,同时我们这一届是第一届这个专业的学生,所以当时专业课已经在大二全部结束,所以我大三可以申请离校找工作,学校的时光异常珍贵,希望学弟学妹们好好珍惜!`_)2016 年 7 月 28 号,我将自己的第一站选择了「北京」,也正是因为自己的自信和能力,以及「大卫哥」的信任,我在 2016 年 8 月 2 号正式入职(_`不是实习哦`_)我的第一家公司 ——「图迹科技」。我的工作生涯,也从此开始。在图迹的一年半时间,我遇到了好领导、好同事,感谢他们对我初入职场的照顾与包容,感恩🙏
### 我的 2019
@@ -105,7 +103,7 @@ top:
3. [magic-starter](https://github.com/xkcoding/magic-starter)
-`magic-starter` 一套神奇的starter,只为爱“偷懒”的你~
+`magic-starter` 一套神奇的starter,只为爱“偷懒”的你~
这个项目的目的是对项目开发中常见的功能的封装,做一套 starter,方便 Spring Boot 项目快速引用集成。主要包括:
@@ -118,7 +116,7 @@ top:
- [ ] 限流组件模块:magic-starter-ratelimiter
- [ ] 分布式锁模块:magic-starter-locker
- [x] 通用对象存储封装模块:magic-starter-oss
-- [x] 分布式主键生成器模块:magic-starter-id
+- [x] 分布式主键生成器模块:magic-starter-id
这套组件暂时还未正式 Release,都还是快照版本。每个模块都配有对应的文档及 demo,小伙伴们敬请期待吧
@@ -143,8 +141,6 @@ top:
-
-
## 看今朝
> 跨入新的 10 年,迎接新的自己!
@@ -181,8 +177,3 @@ top:
附上帅照一张:
-
-
-
-
-
diff --git a/source/_posts/2020-04-01.somewords-about-dissmission-2020.md b/src/data/blog/2020-04-01.somewords-about-dissmission-2020.md
similarity index 87%
rename from source/_posts/2020-04-01.somewords-about-dissmission-2020.md
rename to src/data/blog/2020-04-01.somewords-about-dissmission-2020.md
index 6ec7a97e..3567db47 100644
--- a/source/_posts/2020-04-01.somewords-about-dissmission-2020.md
+++ b/src/data/blog/2020-04-01.somewords-about-dissmission-2020.md
@@ -1,17 +1,15 @@
---
-title: 2020.Q1总结 & 离职感想
-comments: true
-fancybox: false
-toc: true
-date: 2020-04-01 14:42:30
+title: "2020.Q1总结 & 离职感想"
+author: xkcoding
+pubDatetime: 2020-04-01T06:42:30.000Z
+slug: somewords-about-dissmission-2020
+featured: false
+draft: false
tags:
-- 总结展望
-categories:
-- 总结展望
-description:
-permalink: somewords-about-dissmission-2020
-top:
+ - 总结展望
+description: "马云说离职无非两原因:钱给少了、心委屈了。2 年时间,从技术输出到团队管理,为什么最终选择离开?需求不明确的会议、技术推广的阻力、产品质量的无奈……当努力得不到匹配回报,是时候换个环境了。疫情下的职场思考与 Q2 计划。"
---
+
这里先祝大家愚人节快乐。
看了眼上次博客更新的时间,也已经过去了一个季度,真是将“拖字诀”发挥的淋漓尽致。今天聊聊我对这个季度的总结以及自己的一些离职感想。
@@ -55,7 +53,6 @@ top:
5. 2 年时间,我是 4 个产品的后端研发负责人。一款好的产品,应当包含`完整且实际的需求预研、稳定且高效的研发团队、优秀且直观的交互设计、简单的部署体验、省心的运维、牛批的销售`。手里的 4 个产品,我认为没有一项达标,需求不明确、代码不优雅、懵逼的交互、复杂的部署。
我做过的努力:
-
1. 需求不明确,我们组织会议询问需求
2. 代码不优雅,前期我们 2 周一次的 Code Review,后期团队成员固定,减少到 1 月一次
3. 懵逼的交互,我们站在用户的角度给产品经理理清使用流程,查找各种 UI 组件,帮忙寻找解决方案,对了,我们是后端程序猿🤓
@@ -76,4 +73,3 @@ top:
至于我什么时候开始下一份工作?
查看我的完整简历 ,希望可以与您共事!
-
diff --git a/source/_posts/2020-04-20.how-to-design-your-own-rpc-framework-1.md b/src/data/blog/2020-04-20.how-to-design-your-own-rpc-framework-1.md
similarity index 94%
rename from source/_posts/2020-04-20.how-to-design-your-own-rpc-framework-1.md
rename to src/data/blog/2020-04-20.how-to-design-your-own-rpc-framework-1.md
index 45cba7a5..a62749dc 100644
--- a/source/_posts/2020-04-20.how-to-design-your-own-rpc-framework-1.md
+++ b/src/data/blog/2020-04-20.how-to-design-your-own-rpc-framework-1.md
@@ -1,19 +1,14 @@
---
-title: 如何实现一个你自己的 RPC 框架(1)
-comments: true
-fancybox: false
-toc: true
-date: 2020-04-20 16:31:27
+title: "如何实现一个你自己的 RPC 框架(1)"
+author: xkcoding
+pubDatetime: 2020-04-20T08:31:27.000Z
+slug: how-to-design-your-own-rpc-framework-1
+featured: false
+draft: false
tags:
-- java
-- rpc
-categories:
-- 技术相关
-- rpc
-description:
-permalink: how-to-design-your-own-rpc-framework-1
-top:
-
+ - java
+ - rpc
+description: "远程过程调用(RPC)让分布式调用像本地调用一样简单,但底层原理你了解吗?本文从零实现一个 RPC 框架:用 BIO 的 Socket/ServerSocket 做网络通信,JDK 序列化传输数据,动态代理让调用方无感知。三个核心技术点,附完整可运行代码。"
---
## 前言
@@ -71,7 +66,7 @@ public interface IHelloService {
### 2.2. 实现 API
-然后我们在 `rpc-server-bio-provider` 中引入 `rpc-server-bio-api` 这个模块,然后实现 `IHelloService`。
+然后我们在 `rpc-server-bio-provider` 中引入 `rpc-server-bio-api` 这个模块,然后实现 `IHelloService`。
```java
public class HelloServiceImpl implements IHelloService {
@@ -394,4 +389,3 @@ content = hello rpc simple demo
## 示例代码
https://github.com/xkcoding/practice_demo/tree/master/rpc-demo-1
-
diff --git a/source/_posts/2020-05-12.how-to-design-your-own-rpc-framework-2.md b/src/data/blog/2020-05-12.how-to-design-your-own-rpc-framework-2.md
similarity index 93%
rename from source/_posts/2020-05-12.how-to-design-your-own-rpc-framework-2.md
rename to src/data/blog/2020-05-12.how-to-design-your-own-rpc-framework-2.md
index 1ac61da5..10c37a07 100644
--- a/source/_posts/2020-05-12.how-to-design-your-own-rpc-framework-2.md
+++ b/src/data/blog/2020-05-12.how-to-design-your-own-rpc-framework-2.md
@@ -1,19 +1,16 @@
---
-title: 如何实现一个你自己的 RPC 框架(2)
-comments: true
-fancybox: false
-toc: true
-date: 2020-05-12 00:02:27
+title: "如何实现一个你自己的 RPC 框架(2)"
+author: xkcoding
+pubDatetime: 2020-05-11T16:02:27.000Z
+slug: how-to-design-your-own-rpc-framework-2
+featured: false
+draft: false
tags:
-- java
-- rpc
-categories:
-- 技术相关
-- rpc
-description:
-permalink: how-to-design-your-own-rpc-framework-2
-top:
+ - java
+ - rpc
+description: "BIO 一个连接一个线程,大量连接直接崩溃!NIO 同步非阻塞如何解决?本文基于 Netty 重构 RPC 框架:ServerBootstrap 替换 ServerSocket,Channel + Handler 处理请求,事件驱动提升性能。从 BIO 到 NIO 的进化之路,附完整代码对比。"
---
+
## 前言
在上一篇[《如何实现一个你自己的 RPC 框架(1)》](/2020/04/20/how-to-design-your-own-rpc-framework-1.html)中,我们通过 BIO 去实现了一个简单的 RPC 通信案例,今天我们将会基于上次的案例进行改造,将通信由 BIO 改造为 NIO。
@@ -24,7 +21,7 @@ top:
说到 NIO,就需要一起聊聊它的俩兄弟,BIO 以及 AIO。
-BIO:Blocking IO,同步阻塞 IO。简单的说就是`服务端创建一个 ServerSocket,客户端通过 Socket 去连接 ServerSocket,服务端会通过 accept 去阻塞等待客户端的请求`。BIO 存在一个十分明显的问题,就是每当存在一个客户端连接服务端时,服务端都会启动一个线程,专门的去服务这个客户端,这将导致如果出现大量的客户端连接时,服务端将会产生大量的线程开销,导致崩溃。(*因此,在上一篇文章中,我们在服务端使用一个线程异步的去处理这些请求,尽量增大服务端每个线程可以处理的请求数,来达到略微优化的目的*。)
+BIO:Blocking IO,同步阻塞 IO。简单的说就是`服务端创建一个 ServerSocket,客户端通过 Socket 去连接 ServerSocket,服务端会通过 accept 去阻塞等待客户端的请求`。BIO 存在一个十分明显的问题,就是每当存在一个客户端连接服务端时,服务端都会启动一个线程,专门的去服务这个客户端,这将导致如果出现大量的客户端连接时,服务端将会产生大量的线程开销,导致崩溃。(_因此,在上一篇文章中,我们在服务端使用一个线程异步的去处理这些请求,尽量增大服务端每个线程可以处理的请求数,来达到略微优化的目的_。)
NIO:NonBlocking IO,同步非阻塞 IO。在 Java 1.4 中引入,NIO 是基于 Reactor 模型。NIO 中引入了 Selector、Channel、Buffer 等概念。Buffer(缓冲区)主要用来存、取数据,Channel(通道)一个 Channel 对应一个客户端,Selector(调度器)不断轮询 Channel,处理 Channel 发生的事件。在 NIO 模型中,只有当客户端发生一次请求时,才会创建一个线程,请求结束,线程销毁,相比于 BIO 模型一个线程一直阻塞等待一个客户端所有请求而言,减少了线程的开销。NIO 的核心是同步非阻塞,无论多少客户端都可以接入服务端,客户端接入并不会耗费一个线程,只会创建一个连接然后注册到 Selector 上去,一个 Selector 线程不断的轮询所有的 Socket 连接,发现有新的事件就触发通知,然后启动一个线程专门处理一个请求即可,处理完毕释放线程,但是这个处理的过程中,要先读取数据、处理数据、再返回数据,这是个同步的过程。
@@ -235,4 +232,4 @@ content = hello rpc nio demo
## 示例代码
-https://github.com/xkcoding/practice_demo/tree/master/rpc-demo-2
\ No newline at end of file
+https://github.com/xkcoding/practice_demo/tree/master/rpc-demo-2
diff --git a/source/_posts/2020-05-26.how-to-design-your-own-rpc-framework-3.md b/src/data/blog/2020-05-26.how-to-design-your-own-rpc-framework-3.md
similarity index 95%
rename from source/_posts/2020-05-26.how-to-design-your-own-rpc-framework-3.md
rename to src/data/blog/2020-05-26.how-to-design-your-own-rpc-framework-3.md
index 3c0e89bc..cdffdff5 100644
--- a/source/_posts/2020-05-26.how-to-design-your-own-rpc-framework-3.md
+++ b/src/data/blog/2020-05-26.how-to-design-your-own-rpc-framework-3.md
@@ -1,19 +1,16 @@
---
-title: 如何实现一个你自己的 RPC 框架(3)
-comments: true
-fancybox: false
-toc: true
-date: 2020-05-26 14:43:05
+title: "如何实现一个你自己的 RPC 框架(3)"
+author: xkcoding
+pubDatetime: 2020-05-26T06:43:05.000Z
+slug: how-to-design-your-own-rpc-framework-3
+featured: false
+draft: false
tags:
-- java
-- rpc
-categories:
-- 技术相关
-- rpc
-description:
-permalink: how-to-design-your-own-rpc-framework-3
-top:
+ - java
+ - rpc
+description: "NIO 读写数据仍是同步的,AIO 才是真正的异步!本文使用国产框架 smart-socket 重构 RPC:异步 IO 操作,CompletableFuture 阻塞获取结果,MessageProcessor 处理请求响应。BIO → NIO → AIO,三种 IO 模型的 RPC 实战对比系列完结篇。"
---
+
## 前言
在前面的文章[《如何实现一个你自己的 RPC 框架(1)》](/2020/04/20/how-to-design-your-own-rpc-framework-1.html)中,我们通过 BIO 去实现了一个简单的 RPC 通信案例,同时在[《如何实现一个你自己的 RPC 框架(2)》](/2020/05/12/how-to-design-your-own-rpc-framework-2.html)中我们将 BIO 改造成了 NIO,今天我们将会基于第一次 BIO 的案例进行改造,将 BIO 改造为 AIO 通信。
@@ -353,4 +350,4 @@ content = hello rpc aio demo
## 示例代码
-https://github.com/xkcoding/practice_demo/tree/master/rpc-demo-3
\ No newline at end of file
+https://github.com/xkcoding/practice_demo/tree/master/rpc-demo-3
diff --git a/source/_posts/2020-06-01.how-does-my-blog-integrate-cicd.md b/src/data/blog/2020-06-01.how-does-my-blog-integrate-cicd.md
similarity index 94%
rename from source/_posts/2020-06-01.how-does-my-blog-integrate-cicd.md
rename to src/data/blog/2020-06-01.how-does-my-blog-integrate-cicd.md
index 92676aa7..6a578ac8 100644
--- a/source/_posts/2020-06-01.how-does-my-blog-integrate-cicd.md
+++ b/src/data/blog/2020-06-01.how-does-my-blog-integrate-cicd.md
@@ -1,18 +1,15 @@
---
-title: 我的博客是如何集成CICD的?
-comments: true
-fancybox: false
-toc: true
-date: 2020-06-01 23:36:42
+title: "我的博客是如何集成CICD的?"
+author: xkcoding
+pubDatetime: 2020-06-01T15:36:42.000Z
+slug: how-does-my-blog-integrate-cicd
+featured: false
+draft: false
tags:
-- devops
-categories:
-- 技术相关
-- devops
-description:
-permalink: how-does-my-blog-integrate-cicd
-top:
+ - devops
+description: "静态博客发布文章太繁琐?CI/CD 来解放双手!本文分享博客部署演进:v1.0 Jenkins + Travis-CI 自动构建推送 Pages;v2.0 升级 GitLab CI + Docker Swarm 容器化部署,Traefik 反向代理实现 0 配置负载均衡。附完整 Jenkinsfile、.gitlab-ci.yml 配置。"
---
+
## 1. 前言
博客现在貌似成为了一个技术人的标配,从最初的炫技到单纯的记录自己的成长历程。我的博客也经历了很多次「[蜕变](https://xkcoding.com/about/)」。
@@ -198,7 +195,7 @@ pipeline {
${CHANGES_SINCE_LAST_SUCCESS,reverse=true, format="Changes for Build #%n: %c ",showPaths=true,changesFormat="[%a] %m ",pathFormat=" %p"}
+ ${CHANGES_SINCE_LAST_SUCCESS,reverse=true, format="Changes for Build #%n: %c ",showPaths=true,changesFormat="[%a] %m ",pathFormat=" %p"}
@@ -221,7 +218,7 @@ pipeline {