OmniAICreator/Qiita-1.07M huggingface dataset:
【Flutter】Androidのrelease build時にローカル通知の作成に失敗する。

## 課題
Flutterアプリで、flutter_local_notificationsを使ってローカル通知を作成するとき、デバッグビルドでは問題ないが、リリースビルド時に失敗する。


## 確認したこと
まずはリリースビルドの実行
```
$ flutter run --release
```
リリースビルドだとデバッグログが表示されないので、別でターミナルを立ち上げログ監視
```
$ adb logcat | grep flutter
```

すると、下記のようなログが表示される。

```
01-09 14:24:07.177  3966  3966 E MethodChannel#dexterous.com/flutter/local_notifications: Failed to handle method call
01-09 14:24:07.177  3966  3966 E MethodChannel#dexterous.com/flutter/local_notifications: java.lang.RuntimeException: Missing type parameter.
01-09 14:24:07.177  3966  3966 E MethodChannel#dexterous.com/flutter/local_notifications:       at R1.a.<init>(SourceFile:4)
01-09 14:24:07.177  3966  3966 E MethodChannel#dexterous.com/flutter/local_notifications:       at com.dexterous.flutterlocalnotifications.FlutterLocalNotificationsPlugin.loadScheduledNotifications(SourceFile:26)
(略)
```
デバッグビルド時には出なかったタイプエラーが出てる？？なぜ？？

## 原因
この問題は、ProGuard や R8（リリースビルドでコードの最適化、縮小、難読化を行うツール）の設定が原因らしい。
ProGuard や R8 は、Java/Kotlin コードを最適化する過程で使用されていないクラスやメソッド、フィールドを削除するが、特にジェネリクス（List<String> や Map<String, Integer> のような型パラメータ）に関する情報は、ランタイムには不要だと判断されて削除される可能性があるとのこと
https://developer.android.com/build/shrink-code?hl=ja

## 解決策
コードの最適化時に必要な部分が削除されることを防ぐため、proguardの設定を行う。
android/app配下にproguard-rules.proを作成し、下記をコピペする
```
## Gson rules
# Gson uses generic type information stored in a class file when working with fields. Proguard
# removes such information by default, so configure it to keep all of it.
-keepattributes Signature

# For using GSON @Expose annotation
-keepattributes *Annotation*

# Gson specific classes
-dontwarn sun.misc.**
#-keep class com.google.gson.stream.** { *; }

# Prevent proguard from stripping interface information from TypeAdapter, TypeAdapterFactory,
# JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter)
-keep class * extends com.google.gson.TypeAdapter
-keep class * implements com.google.gson.TypeAdapterFactory
-keep class * implements com.google.gson.JsonSerializer
-keep class * implements com.google.gson.JsonDeserializer

# Prevent R8 from leaving Data object members always null
-keepclassmembers,allowobfuscation class * {
  @com.google.gson.annotations.SerializedName <fields>;
}

# Retain generic signatures of TypeToken and its subclasses with R8 version 3.0 and higher.
-keep,allowobfuscation,allowshrinking class com.google.gson.reflect.TypeToken
-keep,allowobfuscation,allowshrinking class * extends com.google.gson.reflect.TypeToken
```
android/app/build.gradleのbuildTypesを下記のように編集する。
```
buildTypes {
    release {
    minifyEnabled true
    shrinkResources true
    signingConfig = signingConfigs.release
    proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
    }
}
```

この設定を行うことで標記の問題は解決できた。

## 参考
https://stackoverflow.com/questions/68265930/java-lang-runtimeexception-missing-type-parameter-error-on-released-apk
https://github.com/MaikuB/flutter_local_notifications/blob/master/flutter_local_notifications/example/android/app/proguard-rules.pro
個人開発　諸学者　SwiftUIブラウザアプリ開発開始1週間　キッズブラウザアプリ

## なにわともあれ、備忘録
人は忘れる生き物なので、無職だと拍車がかかるようで、「昨日やった記憶がまだこほれずに頭の中でこ乗っている」程度には残る。


##　作りたいアプリ
子どもが調べことをしやすいブラウザがあればいいなと思って、検索機能を削減したアプリを作った。

## SwiftUIベースのブラウザアプリ開発の進捗
Githubに乗せているプロジェクトはこれ。
https://github.com/nainai0722/EmawserApp

ブラウザが合って、指定した数件のブックマークのサイトしか閲覧できない時代錯誤なブックマーク設定。

小学年の中高学年には悪くはない品物だと思う。

## ブラウザとの相性であったりフッターの表示非表示の制御もまだまだ検討の余地が大きい。
pixivpyをなるべくシンプルに使ってみる

# 1.はじめに

- pixivのユーザのページのhtmlソースコードを見てリンクを抽出し，画像をDLしようとする．
- 何故かリンクが見付からない．
- 色々調べていくうちに，pixivpyと言うPython用ライブラリを見付ける．
- 使い方を調べるが，難しいので，シンプルなコードを試みる．

･･･と言った流れで，あまり得意ではないPythonで作ったコードを覚書のために公開します．例外処理等の使用者に優しい部分がかなり抜けているので，修正してから使用する事を推奨します．

# 2.参考リンク

以下のQiita記事を参考にしました．本記事ではpixivpyのインストール方法，ログイン方法等は省略します．

https://qiita.com/Yukiho_P/items/1ced158360d88626b052

https://qiita.com/yuki_2020/items/716fa4e4ada65306f688

https://qiita.com/yuki_2020/items/759e639a4cecc0770758

# 3.ソース
```python:pixiv_dl_user.py
import time
from pixivpy3 import *
import sys

args = sys.argv
user_id = int(args[1])
r_token = args[2]
api = AppPixivAPI()
api.auth(refresh_token=r_token)

def check_dl_page(lts_illust_pages, page):
    for c_page in lts_illust_pages:
        if page == c_page:
            return False
    return True

def dl_user_page(api, user_id, latest_illustrations):
    lts_illust_pages = []
    with open(latest_illustrations) as f:
        for line in f:
            lts_illust_pages.append(int(line))
    # Checking latest 120 illustrations
    illust_pages = []
    for page in [30, 60, 90, 120]:
        if page == 30:
            json_result = api.user_illusts(user_id)
        else:
            next_qs = api.parse_qs(json_result.next_url)
            if next_qs == None:
                break
            json_result = api.user_illusts(**next_qs)
        for i in json_result.illusts:
            illust_pages.append(i.id)
    # Checking pages we should download, and written log file
    should_dl_pages = []
    with open(latest_illustrations,"w") as fo:
        for page in illust_pages:
            if check_dl_page(lts_illust_pages, page):
                should_dl_pages.append(page)
            print(page, file=fo)
    return should_dl_pages

def download_image(api, img):
    print("# Download image: %s" % img)
    api.download(img)
    time.sleep(10)

def download_in_list(api, illust_ids):
    for illust_id in illust_ids:
        json_result = api.illust_detail(illust_id)
        illust = json_result.illust
        if illust.meta_single_page:
            download_image(api, illust.meta_single_page['original_image_url'])
        else:
            for s in illust.meta_pages:
                download_image(api, s.image_urls['original'])

latest_illustrations = "latest_illustrations.txt"
illust_ids = dl_user_page(api, user_id, latest_illustrations)
print("# Download page list:")
print(illust_ids)
time.sleep(10)

download_in_list(api, illust_ids)
```

```
python pixiv_dl_user.py [ユーザ番号] [Refresh token]
```
のように入力します．最初に使用する際は，"latest_illustrations.txt"と言う空ファイルを作成しておきます．
実行すると，指定したユーザの最近の120件の投稿イラストをダウンロードします．そのときのイラストのIDはlatest_illustrations.txtに記録され，2度目以降の実行では新規の投稿イラストのみをダウンロードします．
イラスト以外(小説，漫画など)は未対応，前回実行から120件を超える投稿があった場合は無視される，等の仕様となっています．

# 4.終わりに

冒頭にも書きましたが，非常にシンプルに書いたため，使用する際は好みの仕様にアレンジする事をお勧めします．
Rancher Desktopとkindを使ってKubernetesクラスタをたてる

## はじめに

Kubernetesを手元で動かしたい場合、どのように構築するのが良いでしょうか。

以下の手順の通り、Docker Desktopを使うと簡単にKubernetesクラスタを構築することができます。
https://docs.docker.com/desktop/features/kubernetes/
ただ大人の事情でDocker Desktopが使えない場合、他の手段で構築する必要があります。

そのため、本手順ではRancher Desktopとkindを使ってKubernetesクラスタをたてる手順を示します。

## 環境情報

|Component|Version|
|---|---|
| PC | M1 MacBook Pro |
| OS | macOS 15.2 |
| Rancher Desktop | 1.17.0 |
| docker | 27.4.1 |
| kind | v0.26.0 |
| kubectl-cli | v1.32.0 |

## 手順

### インストール

brewでRancher Desktopをインストールします。

```bash
brew install rancher --cask
```

また、kindとkubectlとdockerもインストールします。

```bash
brew install docker kind kubectl
```

### Rancher Desktopの起動と動作確認

Rancher Desktopがインストールできたら、アプリケーション一覧からRancher Desktopを選択し起動します。

起動後はdocker psを実行し、正常終了するか確認します。
```bash
docker ps
```

### kindでKubernetesクラスタをたてる

dockerコマンドが叩けるようになったら、kindでKubernetesクラスタをたてていきます。

以下の通り、kindの設定ファイルをyamlで記述し、kind-sandbox.yamlという名前で保存します。
ちなみに、control plane1つ、worker node2つのsandboxという名前のKubernetesクラスタを作る設定ファイルです。

```yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: sandbox
nodes:
- role: control-plane
- role: worker
- role: worker
```

kind createコマンドを実行して、Kubernetesクラスタを起動します。

```bash
kind create cluster --config kind-sandbox.yaml
```

1,2分ほどで作成が終わるので、以下のコマンドを実行しsandboxが表示されるか確認します。
```bash
kind get clusters
```

最後に、kubectlコマンドを実行できるか確認します。
```
kubectl get nodes
```

## 最後に

本手順でRancher Desktopをkindを使ってKubernetesクラスタをたてることができるようになりました。
kindを使うとKubernetesクラスタの作成と廃棄が簡単にできるようになるので、いろいろと手元で実験してみたいときに役に立ちます。
ナレッジベースツールCosenseの文芸的データベースがすごかったので紹介したい

Helpfeel Techconf 2024の「文芸的データベース - 文書の作成とそのデータベース化を同時に行なう手法」でデモしている、ナレッジベースツールCosenseの新機能がすごかったので紹介します。

> 動画：文芸的データベース - 文書の作成とそのデータベース化を同時に行なう手法
> https://www.youtube.com/watch?v=iTTiivQABQg

Cosenseは、複数人でリアルタイム編集できるWiki（旧名：Scrapbox）。2017年ごろから、読んだ本や見た映画なんかを整理するのに個人的に使っていて、たとえば「#book」というハッシュタグでページを整理できる。

![cosense_pages.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86880/27812f41-820c-9547-fdb8-ce01070e11c8.png)


なんとこれが、ハッシュタグページに次のように書くだけで、**AIパワー**でテーブル表示に変わる。
各ページ側に共通の情報を書き込んでなくても、AIが補完してくれるらしい。

> table:cosense
> 著、作
> 出版社、シリーズ

![cosense_table.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86880/10e4c032-3689-92ca-01a5-7f36f6680625.png)

個別ページ側には、テーブルの共通情報を自動で表示してくれる。これが共通情報を入力しようというモチベになるそうな。

![cosense_item.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86880/da1ea7dc-098d-3fc5-ba15-89a38f77b81f.png)

いちいちデータベースを整備したり、文書ページ側にメタ情報を書かなくても、各ページにメモを蓄積していくだけでデータベースになる。テーブル化する項目は、後から自由に変更できる。

社内Wikiや共有フォルダの整理が苦手な人にぴったり！

Cosenseを、シンプルで柔軟な個人用Wikiとして今まで使ってきたけれど、いつの間にかスゴイ機能がいっぱい実装されていて。モバイルでも使いやすくなってる。もうちょっと使い倒してみたい。
勉強開始宣言

データサイエンス＆機械学習を勉強しようと思ってたところ、Udemyでキャンペーンを見つけたんで、関連講座をいくつか買ってみました。

買ったのは以下の5つです。

・Python for Machine Learning & Data Science Masterclass
・Python for Data Science and Machine Learning Bootcamp
・The Data Analyst Course: Complete Data Analyst Bootcamp
・The Data Science Course: Complete Data Science Bootcamp 2025
・The Complete Python Bootcamp From Zero to Hero in Python

選んだ理由は、データサイエンスと機械学習のカテゴリで人気が高かったやつを適当にピックアップしたから。同じような内容の講座がいくつかあるけど、正直言うと機械学習とデータサイエンスの違いすらよく分かってないんで、「とりあえず両方やっとけ！」って感じで、完全初心者からのスタートです。

このブログを始めた理由は、学習記録を付けてみたかったのと、「やるぜ！」って公開宣言しておけばサボったときにバレる仕組みを作って、三日坊主を防ぎたいからです。

とは言っても平日は仕事があって、なかなか自主学習の時間を取るのが難しいので、3日くらい空いちゃうこともあるかも。でも、「三日坊主」はあくまで比喩ということで勘弁してもらって、長期間放置しないように気をつけて頑張りたいと思ってます。

ざっくり自己紹介すると、今までは組み込みソフトウェアの設計開発をやってきて、C言語ならある程度読めるレベルです。Pythonは会社の研修でちょっと触ったぐらいで、ほぼ初めてに近い状態です。

趣味は旅行とかテニスとか楽器（フルート）とかいろいろやってます。ただ、このブログには趣味の話題はあんまり書かないかも。他の人の記事をちゃんと見れてないけど、技術系の記事がメインっぽいんで、趣味の話を書くと場違いになりそうな気がしてます。でも、堅苦しくなりすぎないように、たまには息抜きの話も入れたいなと思ってます。

今回は活動宣言だけで、学習記録は次回以降にまとめる予定です。これからよろしくお願いします！

初心者で「一緒に勉強しよう！」って方や、「教えてやるぜ！」っていうベテランの方は、ぜひフォロー（？っていうのかな？）してもらえると嬉しいです (*'▽')
Windows 11で音声デバイスをつなぐ

# 第一段階 USB Bluetoothで接続
今回の記事はWindows11で確認しているが、当然Windows10でも
とにかく接続するというのがまず第１段階。
特にBluetoothでトラブルことが多い。
手順は複数あるが、一つに絞る。
ヘッドセット
<ol>
<li><b>スタート</b>
<li><b>設定</b>をクリック(個人用設定 スタート フォルダーで表示するがオンの時）
<li><b>デバイス</b>をクリックする　「Windowsの設定」の上段左から2番目にあるオプション
<li><b>Bluetoothとその他のデバイス</b>をクリック
<li>Bluetoothが有効(青色)になっているか確認、なっていなければオン。
<li>ヘッドセットの電源を入れてBluetoothでのペアリング開始
<li>ヘッドセットをペアリングモードにする
<li>ヘッドセットが「デバイスを追加する」画面に表示されたら、それクリックして追加
</OL>

## 問題はペアリングモード
　これはヘッドホンのメーカーや機種によって異なる。安物であれば電源ボタンを３秒押すとか。
 　また、一度接続するとＰＣが記憶？するので、その後人間がペアリングの仕方を忘れることもある。
  [Bluetoothの「ペアリング」と「接続」ってなに？ | 使いかた | ヘッドホン | サポート・お問い合わせ | ソニー](https://www.sony.jp/support/headphone/howto/bluetooth/?srsltid=AfmBOorKoeyudF4ykM1DSm3OyLxRsXOqFNQ3ivB2WrQqsaaLvKPrZjuq)
[ペアリングする（機器登録する） | HA-FX100T | 取扱説明書(Webマニュアル編)](https://manual3.jvckenwood.com/accessory/headphones/contents/ha-fx100t/jp/BONDSYuwdpeusm.php)
[Bluetoothヘッドセットの仕様、ペアリング方法一覧 Elecom](https://qa.elecom.co.jp/faq_detail.html?id=7042)
など

## USBは刺すだけ
USB-AかUSB-Cかくらいしかない。
## ディスプレイが３種類
マルチモニターだとデバイスが１つ多くなる。しかもUSB-C、HDMI、Displayportがある。
ここでも音声の出力デバイスの切り替えが必要となる。

## アナログも刺すだけ
最近はマイクとスピーカーが1つになり、３極、４極、５極まであるが、５極はソニーのノイキャン系で、こんなものはBluetoothにとって代わられそうなので、３極か４極という状況。
[完全ワイヤレス型ヘッドホン新フラッグシップモデル『WF-1000XM5』の生み出す究極の静寂と音【ノイズキャンセリング性能・高音質編】 | ソニー](https://www.sony.jp/feature/products/headphone/230726_01/?srsltid=AfmBOoo76SOUoUBVpq8i5fgLzV-yw4TdAK0XDQQY4riAJ5LEkj2kIi51)
こういう記事で5極が消えるフラグを感じるのは筆者だけだろうか？
５極はさておき、本来、PCでは、音声の入出力はアナログというかラインというか、音声の端子につなぐのがセオリーで、USBもBluetoothも邪道であった。
これはなぜかというと、デバイスの割り当ては必ずPCのリソースを食い、そして音声はDA/DA変換が必要になり、これは割と大きい。なのでトラブルの原因となる。わざわざPCで用意しているのに、USBやBluetoothでリソースを食うというのはいかにもおかしな話である。
とはいうものの、３極、４極あたりで混乱が生じてしまい、USBに付け入られる隙を作ってしまったと思う。さらにワイヤレスが混迷を深めた。


## おおむね4種類
以上のように、音声のデバイスの切り替えは、ディスプレイ(HDMI等)、ラインの入出力、USB、bluetooth、が接続されるため、デバイスの切り替えが必要となる。

## HDMIも音声出力
ここで前述のとおり、モニターにもスピーカーがあり、こちらから音が出る。
そうするとおおむね５種類くらいのデバイスがPCにつながっているという状況になる。
現在はこのように複数のデバイスが接続されており、次のデバイスの選択が必要となる。

# 第二段階
以前ならここでめでたしめでたしだった。
しかし、例えばマルチディスプレイにすると、出力デバイスはモニター１、モニター２、ヘッドセットのスピーカと３つある。これを切り替える必要がある。

## ミキサーの設定の確認
設定 システム サウンド 音量ミキサーを開く
で出力デバイス、入力デバイスともBluetoothまたはUSBのヘッドセットになっているか確認する
そしてここでやはりQiitaである以上、Win+Rに入れるワンライナーを書く必要があるし、わかりやすい。

```bat
%comspec% /c "start ms-settings:sound"
```

となる。これを書かないと、これまた複数の画面が表示され、スクショを取らないといけないが、スクショをとったからと言ってそこに行ってくれるとは限らない。そんなわけでファイル名を指定して実行が役立つ。

### こちらではない
Mixiserだけ開く sndvol.exe
https://www.tunesbro.jp/blog/how-to-open-the-volume-mixer-in-windows-11.html
以前のサウンドのミキサーが開くが、デバイスの切り換えできない。

# 第三段階 アプリの設定の確認
通常、ここは既定になっていると思う。しかし以下で説明されているとおり、
[NEC LAVIE公式サイト > サービス＆サポート > Q&A > Q&A番号 023678](https://faq.nec-lavie.jp/qasearch/1007/app/servlet/qadoc?QID=023678)
Windows10/11ではアプリごとにスピーカーを設定することができる。

```bat
%comspec% /c "ms-settings:apps-volume"
```
最もこれで開くのは
システム サウンド 音量ミキサー
であり、そのページの下の方にアプリ という項目がある。そこで設定ができるというわけである。
なので、

```bat
%comspec% /c "start ms-settings:sound"
```

こちらだけ覚えておいてもよい。

## <font color=red>設定厳禁</font>
この設定はよほどPCとアプリが限定して使用しているような状況しか適用する意味がないことから、通常はここの設定を既定から変えないこと。

# こういうのに疲れたら
このように設定も設定の変更も面倒なので、アナログ端子にみんなつなごう。とにかく<b>自分で持ってきたbluetoothヘッドフォンがつながらないという文句を他人に言うのはマジでやめろ。</b>
【備忘録】nodeのfsにあるPOSIX関数ってなんですか？

## 始まり
コーティングテストを勉強している時にjavascriptでnode.jsでfsモジュールというのが出てきました。
ファイルを扱うためのモジュールということで公式のドキュメントを確認したところ表題の疑問が沸きましたので、備忘録として残します。

https://nodejs.org/api/fs.html

## そもそもfsとはFile system
ドキュメントによると”このモジュールを使用することで、標準のPOSIX関数をモデルにした方法でファイルシステムと対話できるようになります。

#### File systemとは
OSが提供する機能の一つで、記憶媒体に保存されたデータを管理、操作する機ために必要な機能です。

## POSIX関数ってなんですか？
Portable Operating System Interfaceの略であり、主要なOSの共通機能にたいして互換性のある、標準化されたインターフェイスを提供するAPIの集合です。
これはIEEE（米国電気電子学会）によって定められた標準規格です。
VSCodeでPlantUMLを含むMarkdownの閲覧環境を構築【Markdown Preview Enhanced】

VS Codeに「Markdown Preview Enhanced」拡張機能を導入して、PlantUMLを含むMarkdownを快適に閲覧する環境を構築する方法を示す。

## Markdown Preview Enhancedとは

標準のマークダウンビューワでは表示できない記法などを拡張する拡張機能。 コードブロックに行数表示を加えたり、数式の挿入が可能となる。

## 想定環境

- Windows 11
  - PowerShellからwingetが利用できること

## 手順

### 1. Javaをインストール
以下のコマンドを実行し、Javaをインストールする。  

```
winget install Microsoft.OpenJDK.21
```

※適宜、最新のバージョンをインストールすること。

### 2. Graphvizをインストール
以下のコマンドを実行し、Graphvizをインストールする。  

```
winget install Graphviz.Graphviz
```

### 3. PlantUMLのJarを配置
以下から最新のPlantUMLのJarファイル（`plantuml-1.2025.0.jar`など）をダウンロードして、フォルダに配置する。

https://github.com/plantuml/plantuml/releases


### 4. VS Code拡張機能のインストール

VS Codeで「Markdown Preview Enhanced」拡張機能をインストールする。  

https://marketplace.visualstudio.com/items?itemName=shd101wyy.markdown-preview-enhanced

### 5. 設定を調整

VS Codeで`Ctrl + ,`を押してSettingsを開き、`markdown-preview-enhanced.plantumlJarPath`の項目に、jarファイルの絶対パス（`C:\MyPrograms\plantuml-1.2025.0.jar`など）を設定する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186338/36ce5de8-f082-c58f-fc7a-953738c8daf0.png)

## 動作確認

Markdownファイルを作成し、次のコードブロックを追加する。  

---

&#96;&#96;&#96;plantuml
@startuml
Alice -> Bob: Hello
@enduml
&#96;&#96;&#96;

---


Markdownファイルを開き、`Ctrl + Shift + V`を押してMarkdown Preview Enhancedを開き、以下のようなPlantUMLの図が表示されることを確認する。

```plantuml
@startuml
Alice -> Bob: Hello
@enduml
```
initとstatic funcの使い分け(Swift)

## はじめに

ふと「イニシャライザ（コンストラクタ）と `Self` を返すstatic funcの使い分けって何だろう？」と思ったので、自分の言葉でまとめてみます。

https://x.com/the_uhooi/status/1876865206120222961

あくまで自分の考えなので、参考程度に留めてもらえればと思います。

## 環境

- OS: macOS Sonoma 14.5
- Xcode: 16.2 (16C5032a)
- Swift: 6.0.3

## 注意

あくまで「 `Self` を返すstatic func」とイニシャライザの比較なので、BuilderパターンやFactory Methodパターンなどの複雑なパターンについては本記事では考慮しません。

## 結論

まず結論を述べます。
static funcはstatic letやstatic varも含めます。

- インスタンスに名前を付けられる → static func
- インスタンスに名前を付けられない → init

たったこれだけです。

## インスタンスに名前を付けられる

インスタンスに名前を付けられる場合はstatic funcを使います。
この説明だけではピンと来ないかもしれないので、具体例を挙げます。

### 例1: 特別な値を返す

特別な値を返す場合、意味がわかるように名前を付けます。

例えばSwiftUIの `Color` で赤を返す場合は `red` 、青を返す場合は `blue` と名付けられるのでstatic funcとします。

```swift:定義
public struct Color {
    public static let red: Self = ...
    public static let blue: Self = ...
}
```

```swift:呼び出し
Text("Foo")
    .foregroundStyle(.red)
```

initでも実現できますが、冗長になります。

```swift:❌定義（冗長）
public struct Color {
    public enum Type {
        case red
        case blue
    }

    public init(type: Self.Type) {
        switch type {
        case .red: ...
        case .blue: ...
        }
    }
}
```

```swift:❌呼び出し（冗長）
Text("Foo")
    .foregroundStyle(.init(type: .red))
```

### 例2: Singletonパターン

よく使われるパターンとして、Singletonパターンを紹介します。
`shared` や `current` などと名付けることが多いです。

Singletonを実現するため、initはprivateにします。

```swift:定義
public final class FooDataSource {
    public static let shared: FooDataSource = .init()

    private init() {}

    public func foo() {
        ...
    }
}
```

```swift:呼び出し
FooDataSource.shared.foo()
```

これでSingletonのインスタンスを呼び出しているとわかりやすくなります。

## インスタンスに名前を付けられない

インスタンスに名前を付けられない場合はinitを使います。

### 例1: カラーコードから色を生成する

例えば16進数のカラーコードから `Color` を生成する場合、カラーコードによって赤にも青にもなり得るため、名付けられません。

```swift:定義
public struct Color {
    public init(hex: String) {
        ...
    }
}
```

```swift:呼び出し
let red: Color = .init(hex: "#FF0000")
```

### 例2: 引数を保持するだけ

渡された引数をそのまま保持したり、少しだけ加工したりする場合も、名付けられないのでinitを使います。

```swift:定義
public struct Foo {
    public let title: String
    public let description: String
    public let url: URL?

    public init(
        title: String,
        description: String,
        urlString: String
    ) {
        self.title = title
        self.description = description
        self.url = URL(string: urlString)
    }
}
```

```swift:呼び出し
let foo: Foo = .init(
    title: "Foo",
    description: "Bar",
    urlString: "https://example.com"
)
```

よく見るinitだと思います。

## おわりに

自分なりにinitとstatic funcの使い分けを考えてみましたが、どうでしょうか。
名付けられなくても加工する処理が重い場合はinitでなくstatic funcを使うなど、他の観点もあると思います。

ぜひ本記事と異なる観点があればコメントなどで教えてもらえると嬉しいです :relaxed:
【UE5】Add Unique で作る同じ値の出ない乱数

## 今回すること
Int変数配列とAdd Uniqueを使い込ま、同じ値の出ない乱数を作ります
## Add Unique の軽い説明
このノードは配列に要素を追加するだけのAddの亜種のようなもので、
Add Uniqueは追加しようとした要素が既に配列内にある場合追加せずintで-1をかえしてくれるます
今回作る物には持ってこいなノードです
## 全体図
![1000010005.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3981899/ba498abf-9583-ff2e-a99c-9c62c0310180.png)
Random IntoegerのMAXの変数が見切れている先ではint変数が繋がっています。
あと変数の名前は普通に英語にしましょう。
常識ですよね…はい。

## 解説
Random Intoegerで適当な自然数を出しhennnahyouzi変数にセット
Add Uniqueで先程セットした変数を変数保存変数の配列に要素として追加(しようとする)
ブランチでAdd Uniqueの結果が-1(すでに配列内にある値)なら、フォルスを通り、最初のRandom Intoegerで値を振り直します
結果がそれ以外なら、トゥルーを通り次の処理に行くか終わりです。

## 使用例
ルーレット、ランダムエンド分岐、8番ライクを作る時の異変のフラグ管理…等？
筆者はこの辺りしか思いつきませんでした。

## あとがき
使用例の中にない事をするために1度出た値を出さない乱数が欲しく、色々調べてもUEで作っている記事が出てこなかったので作るか！と思いこの記事を作成しました。
気が向けば私が作っているものの記事を作っても良いかもしれない
(ただ、かなり複雑でかなりパッパラパーなものなので時間がかかるかもです)
复习笔记3-面向对象高级

## 1.static（静态）
#### ①修饰成员变量
有stati修饰：类变量（在计算机里只有一份，会被类的全部对象共享）
某个数据只需要一份，且希望能够被共享（访问，修改），则该数据可以被定义成类变量来记住。
⭐类名.类变量
```java
static String name;
```
无stati修饰：实例变量（属于每个对象的）
⭐对象.实例变量
```java
int age;
```
#### ②修饰成员方法
有stati修饰：类方法，属于类
⭐类名.类方法
无stati修饰：实例方法，属于对象
⭐对象.实例方法
## 2.继承(extends)
让一个类与另一个类建立起父子关系。
#### 特点：
子类能继承父类的非私有成员（public:成员变量/方法）
## 3.方法重写
当子类觉得父类中的某个方法不好用，或者无法满足自己的需求时，子类可以重写一个方法名称，参数列表一样的方法，去覆盖父类的这个方法。
重写后，方法的访问，java会遵循就近原则
⭐注意事项：
①使用@Override注解
②子类重写父类方法时，访问权限必须大于或等于父类该方法的权限
③重写方法的返回值类型，必须与被重写方法的返回值类型一样，或者范围更小
④私有方法，静态方法不能被重写
⭐常见应用常见
子类重写Object类的toString()方法，以便返回对象的内容
## 4.子类中访问其他成员的特点
①在子类方法中访问其他成员（成员变量，成员方法），是依照就近原则的
②在子类中指定访问父类的成员：super.父类成员变量/父类成员方法
## 5.子类构造器的特点
子类的全部构造器，都会先调用父类的构造器，再执行自己
## 6.多态
#### ①前提
⭐有继承/实现关系
⭐存在父类引用子类对象
⭐存在方法重写
#### ②问题
多态下不能直接调用子类的独有方法
## 7.多态下的类型转换问题
#### ①类型转换
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/67e5ecfd-b4ee-a8d7-2e34-57e2f73b5f44.png)
强转前，Java建议：
使用instanceof关键字，判断当前对象的真实类型，再进行强转
```java
  p instanceof Student
```
## 8.final
#### ①修饰类：最终类，不能被继承了
#### ②修饰方法：最终方法，不能被重写了
#### ③修饰变量：该变量只能被赋值一次
## 9.常量
使用了static final修饰的成员变量就被称为常量
作用：通常用于记录系统的配置信息
命名规范：使用大写英文单词，多个单词使用下划线连接
```java
   public static final String SCHOOL_NAME = "传智教育";
```
## 10.抽象类(abstract)
abstract修饰类，这个类就是抽象类
abstract修饰方法，这个方法就是抽象方法（不能有方法体）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/e93e87d0-50bb-59d2-1b33-9c6ea4efcd61.png)
⭐
①抽象类中不一定有抽象方法，有抽象方法的类一定是抽象类
②抽象类不能创建对象，仅作为一种特殊的父类，让子类继承并实现
③一个类继承抽象类，必须重写完抽象类的全部抽象方法，否则这个类也必须定义成抽象类
④场景和好处：父类知道每个子类都要做某个行为，但每个子类要做的情况不一样，父类就定义成抽象方法，交给子类去重写实现。
## 11.接口（interface）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/7356e4f3-b5ca-1e6a-3af6-e554c37c8d0f.png)
⭐接口不能创建对象；接口是用来被类实现的，实现接口的类称为实现类
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/7d8318ca-b699-cb34-6131-60a66dad3684.png)
一个类可以实现多个接口，实现类实现多个接口，必须重写完全部接口的全部抽象方法，否则实现类需要定义成抽象类
⭐
继承是单继承，实现是多实现
接口不能继承，只能实现
## 12.接口的多继承及注意事项
①一个接口可以同时继承多个接口
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/b73a6cbd-0c63-14b6-2e33-b0305e2c30e5.png)
## 13.泛型
可以避免强制类型转换，及其可能出现的异常
本质：把具体的数据类型作为参数传给类型变量
```java
        // <String>：list里面只能放String类型的数据
        ArrayList<String> list1 = new ArrayList<>();
        list1.add("java1");
        list1.add("java2");
        list1.add("java3");
        for (int i = 0; i < list1.size(); i++) {
        // 遍历出来的数据交给String
            String e = list1.get(i);
            System.out.println(e);
        }
```
⭐泛型不支持基本数据类型，只能支持引用数据类型
复习笔记5-特殊文件 日志技术 正则表达式 异常 多线程

## 1.Properties属性文件
⭐Properties是用来代表属性文件的，通过Properties可以读写属性文件里的内容
#### 特点：
①属性文件的内容都是一些键值对信息（键不能重复）
②属性文件的后缀，一般都是.Properties结尾的
## 2.XML文件
⭐本质是一种数据的格式，可以用来存储复杂的数据结构，和数据关系
(经常用来作为系统的配置文件，或者作为一种特殊的数据结构，在网络中进行传输)
#### ①特点：
1.标签名可以自己定义
2.只能有一个根标签
3.标签可以有属性
4.后缀.xml
#### ②语法规则：
1.文档声明必须是第一行
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/31337651-1e7c-3c32-28bc-2c806d64af8f.png)
2.特殊字符
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/e3bba8b0-72e8-c50a-9b82-a279b6a8b02a.png)

## 3.日志技术
程序中的日志，通常就是一个文件，里面记录的是程序运行过程中的各种信息
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/2eeac0a2-4292-b1bd-823e-c2fe422ba52e.png)
## 4.正则表达式
就是由一些特定的字符组成，代表的是一个规则
#### 作用：
①用来校验数据格式是否合法
②在一段文本中查找满足要求的内容
## 5.异常(Exception)
⭐抛出异常(throws)：在方法上使用throws关键字，可以将方法内部出现的异常抛出去给调用者处理
⭐捕获异常(try - catch)：直接捕获程序出现的异常
#### 自定义异常的种类
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/416f0ce3-e7a4-76e1-9a47-a63b7da6023d.png)
复习笔记9-Vue Ajax Axios 前端工程化 Vue项目/组件/路由 打包部署 Maven依赖

## 1.Vue(html,js,css的结合体，框架！)
#### ①Vue入门三部曲
😊引入Vue.js文件
😊创建Vue核心对象，定义数据模型
😊编写视图
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/551b4860-a361-08d0-11de-e785d9ab692f.png)
#### ②常用指令
通过v-bind或者v-model绑定的变量，必须在数据模型中声明
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/0c7600dd-c407-44da-f59c-6ce567d8b60a.png)
#### ③生命周期
mounted:挂载完成（发送请求到服务端，加载数据）
## 2.Ajax
⭐作用：
给服务器发送请求，并获取服务器响应的数据
异步交互：如搜索联想，用户名是否可用的校验等
## 3.Axios
①引入Axios的js文件
②使用Axios发送请求，并获取响应结果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/dc0daab2-65fe-313b-568e-af721e6cea31.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/10e56f7e-9686-a4ff-d266-e58f922592c9.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/3021d447-03d8-c37c-b21b-7513386f93a4.png)
## 4.前端工程化
环境准备：
下载：Nodejs 
安装：vue-cli(用于快速生成一个Vue的项目模板)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/611a3897-004a-365c-a478-8a1bc22481b7.png)
## 5.Vue项目简介
命令行启动：npm run serve 终止：ctrl+c
⭐新建一个vue项目：
①建工程：文件夹内打开命令行，输入vue create 项目名称（vue-project01）
②在vscode中打开工程文件夹
③创建vue组件，输入三件套
😊三件套：
①< template > :html代码
②< script >:创建对象
③< style >:css样式
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/37849dbb-beab-ca8a-2723-fcc1a1384957.png)
## 6.Vue组件库Element
https://element.eleme.io/#/zh-CN/component/installation
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/0e7f116c-efe7-98ee-908b-1e7d18b1ab3d.png)
ctrl+alt+l:代码格式化
## 7.Vue路由
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/89b42910-7939-883e-8cff-33a96a7a6e2f.png)
## 8.打包部署
⭐打包：
命令行：npm run build（打包出来的文件在dist中）
⭐部署：
①打包好的dist目录下的文件复制到NGINX的HTML文件夹中
②双击nginx文件
## 9.Maven依赖
https://mvnrepository.com/
IDEA的使用

# 一：项目初始配置
### 1.Demo项目介绍
⭐项目框架： 
①SpringBoot:几分钟内就搭好一个可以运行的项目
②Mybatis:用于对数据库的访问（持久层的框架）
③Devtools:部署工具，每次改代码不需要重启，idea会自动重启
（pom文件：添加依赖）



⭐分层结构（6层）：
①Config层:存放SpringBoot相关的配置类，包括启动类
启动类注解：
@SpringBootApplication
@ComponentScan（“com”） 扫描，放在com下的类全部交给Spring管理
@MapperScan("com.imooc.demo.mapper")   Mybatis扫描的包
②Controller层:所有请求的入口，前后端交互的入口
注解：@RequestMapping("/login") 前端通过这个注解进到程序中来
③Service层:负责所有的业务逻辑
负责Domain 与 Dto的转换
④Mapper层：负责java和数据库交互（包括interface（java的接口层）和xml（写sql语句）两类文件）
⑤Domain层：用一个java类来映射数据库表，类名就相当于表名，类的属性就相当于表的字段
⑥Dto层:数据传输对象，用于前后端数据交互（实体类）
和表并不完全一致，可以适当增加减少。 用在Controller和Service层



⭐application.properties文件配置：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/52d57d35-2c4a-50e3-765b-7f7e18cada39.png)



⭐gitignore:不需要交给git管理的文件放在这里


### 2.常用的基本设置
编码改为UTF-8:
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/3b25731b-d100-f590-36b0-4586109374ce.png)
jdk设置：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/be0916c8-bb0b-4ee1-61be-b3990c50d827.png)
关联import 自动导入
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/a3c4430b-529d-cd51-c085-297a360cfdbd.png)
### 3.编辑区设置
代码自动换行：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/7f9fcf7a-e07b-11e7-7016-29fa5d0538a4.png)

# 二：开发中必会的技能
### 1.高效的代码编辑技能
⭐格式化代码：Ctrl+Alt+L
⭐复制：Ctrl+D
⭐上下移动一行或多行：Alt+Shift+⬇/⬆
### 2.快速跳转
⭐跳到指定行：Ctrl+G
⭐tabs快速切换：Alt+左箭头/右箭头
⭐查看浏览过的文件：Ctrl+E
⭐打开文件/类所在文件夹：Show In Explorer
(项目路径不要带中文)
### 3.快速查找和替换
⭐查找内容：Ctrl+F  全局查找：Ctrl+Shift+F
⭐替换内容：Ctrl+R  全局替换：Ctrl+Shift+R
⭐按文件名查找文件：Ctrl+Shift+N
⭐查找所有的菜单或操作：Ctrl+Shift+A
⭐万能查找：查找文件，菜单，操作（不能查找文件内容）：连按两次Shift
### 4.万能快捷键 Alt+Enter
⭐见到红色报错就按这个快捷键
⭐见到波浪线警告就按这个快捷键
⭐没报错没警告也可以按
### 5.键盘鼠标的配合
⭐纵向选择多列：按住Alt键不放，鼠标点击拖动
⭐选中一大段代码：鼠标点击开始位置，找到尾行的位置，按住Shift+鼠标点击结尾位置
⭐快速移动复制代码：用【选中一大段代码】的方法，要选择到最上一行的结尾，光标定位到目标位置的上一行结尾，粘贴
⭐类或方法的跳转：Ctrl+鼠标左键
# 三：运行与调试
自动编译：  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/47eb1cce-0c28-4b94-d6b9-3497d1431ab1.png)
# 四：高级进阶
### 1.代码生成Generate
生成set/get，构造函数，toString:Alt+Insert
### 2.代码重构Refactor
⭐重命名(局部变量，方法)：Shift+F6
⭐自动生成返回值：Ctrl+Alt+V
⭐移动文件位置：F6
⭐撤销上一步操作：Ctrl+Z
⭐文件复制:F5
⭐文件删除：Alt+delete
# 五：常用插件
集成git插件
①本地开发环境相关的文件不要提交。包括build,target,class等编译后生成的文件夹和文件
②新建项目时，README文件和.gitignore文件都不要
第一章 SpringBoot项目搭建

## 创建SpringBoot项目
#### 1.创建方法：
官网：https://start.spring.io/
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/d610408c-24c4-dfab-3761-c7e5cc449a9f.png)
点击完成后会自动下载一个压缩包→解压→用IDEA打开即可。
注意：SpringBoot不需要配置容器，使用了嵌入式容器，默认使用tomcat启动，默认端口8080。（容器：用于启动java web项目）
#### 2.项目初始配置：
⭐编码配置：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/8f7fbccd-6cb9-6ae8-db20-ae874a61597b.png)
⭐JDK配置：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/ccf2274c-4ff9-f317-2177-f60a4c43b3eb.png)
⭐Maven配置：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/bd4152dc-e071-0f20-3963-3b3c9dc14725.png)
⭐Git配置：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/06f80e1a-cb3d-9217-c918-02d3172c0f0c.png)
#### 3.开发Hello World接口
⭐注解：
①ResponseBody:用于声明返回文本数据，一般返回JSON
②RestController：Controller层的注解（Controller层用于定义接口，是请求的入口）
③RequestMapping：要让方法变成一个接口（支持所有的请求方式）
常见的http请求方式：get查询 post新增 put修改 delete删除 
④ComponentScan：配置要交给Spring管理的类路径
SpringBootApplication注解包含了ComponentScan注解
如果启动类和其它类所在的包没有包含关系的话，需要加ComponentScan注解
注意：扫描的路径不要太通用，比如扫描"com.jiawa"，而不是"com"（会把第三方jar里的类也扫描出来）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/a47a2a98-2fc7-c678-c3ac-d61972449793.png)
注意：config层用于放配置类
#### 4.自定义配置项
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/ca29094f-bd24-526d-2c74-1953deef8c8e.png)
#### 5.集成热部署
①在pom文件中添加依赖
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/2216c08b-100e-ad77-4a8f-58d0a8189f69.png)
②
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/83d18e68-7828-cae8-a2ad-e8f2f71b8fd5.png)
③
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/b3d254bb-fad8-1d00-1954-8127dfc9c83e.png)
第三章  Vue3+Vue CLI项目搭建

## 1.创建Vue CLI项目
#### ①安装Vue CLI 
提前安装好Node.js
IDEA的Terminal命令行：
```
npm install -g @vue/cil
```
#### ②创建web应用(项目名称：web)
IDEA的Terminal命令行：
```
vue create web
```
#### ③启动web应用
进入web
```
cd web
```
启动web
```
npm run serve
```
## 2.集成Ant Design Vue
官网：https://antdv.com/docs/vue/getting-started-cn#%E5%BC%95%E5%85%A5-ant-design-vue
#### ①安装流程
进入web
```
cd web
```
安装Ant Design Vue
```
 npm i --save ant-design-vue@4.x
```
#### ②使用Vue默认的模板语法，需要注册组件后才可使用，方法如下
（集成第三方组件，一般都在main.ts里加配置）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/67a69939-6bca-1d1f-c9de-6a0c07b1f9b5.png)
⭐在主页中配置按钮，复制模板语法到Home.vue文件中，执行web。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/7ed889a1-2240-74c8-f140-c56db5a55dda.png)
## 3.网站首页布局开发
#### 1.加入Ant Design Vue布局
将布局写在App.vue,将各页面变化的部分放到各自路由页面
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/e1d52cf6-69bf-9f6a-c01f-fbb7b5e577b6.png)
①在App.vue中粘贴布局代码及style,css代码
②添加首页路由：
⭐：router-link标签：路由的跳转（超链接）
⭐：router-view标签:用来填充路由内容
注意：头部尾部是公共部分不变的，所有页面都有的。
     中间部分的sider及Content，是变动的，需要设置路由
在App.vue中，将sider及Content剪切，并在当前位置添加router-view标签
sider及Content剪切到home主页的template标签中，用a-layout标签包起来
## 4.制定Vue自定义组件
公共的代码做成公共的组件(组件的名字：the-xxx)
#### ①将header提取成组件
⭐在components包下新建the-header.vue文件
⭐在文件的template标签中：写头部的代码（在App.vue中剪切过来）。添加script部分：：把name改为the-header
```vue 
<script lang="ts">
import { defineComponent } from 'vue';

export default defineComponent({
  name: 'the-header',
});
</script>
```
⭐在App.vue中，导入头部组件。在原来的头部位置处添加the-header标签。添加script部分：（绿色部分为改动部分）
```diff
<script lang="ts">
import { defineComponent } from 'vue';
+ import TheHeader  from "@/components/the-header.vue";
export default defineComponent({
+  name: 'app',
  components:{
+   TheHeader,
  },
});
</script>
```
注意：引入组件步骤：
1.import组件页面
2.comonents加入该组件
3.在template中使用组件
#### ②将footer提取成组件
步骤同上：唯一不同在于
```diff
<script lang="ts">
import { defineComponent } from 'vue';
import TheHeader  from "@/components/the-header.vue";
+ import TheFooter  from "@/components/the-footer.vue";
export default defineComponent({
  name: 'app',
  components:{
    TheHeader,
+    TheFooter,
  },
});
</script>
```
第六章  分类管理功能开发

## 1.分类表设计（两级分类）与持久层代码生成
#### ①在DBeaver中生成表并插入数据
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/358503ab-f604-72ce-d578-cbd5b7157e14.png)
#### ②生成器配置文件中（generator-config.xml）:
生成对应的四个类
```java
<table tableName="category"/>
```
## 2.完成分类基本增删改查功能
⭐从电子书管理复制
#### ①controller层下新建CategoryController类
#### ②req下新建CategoryQueryReq类：重写tostring方法
#### ③req下新建CategorySaveReq类
#### ④resp下新建CategoryQueryResp类
#### ⑤service层下新建CategoryService类
#### ⑥在components/the-header中新增目录
```java
<a-menu-item key="/admin/category">
       <router-link to="/admin/category">分类管理</router-link>
</a-menu-item>
```
#### ⑦在router/index中新增路径
```java
import AdminCategory from '../views/admin/admin-category.vue'

{
    path: '/admin/category',
    name: 'AdminCategory',
    component: AdminCategory
  },
```
#### ⑧增加分类管理页面：admin-category
#### ⑨为解决id精度问题增加JacksonConfig类（雪花算法产生的问题）
```java
package com.jiawa.wiki.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;


@Configuration
public class JacksonConfig {
    @Bean
    public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
        ObjectMapper objectMapper = builder.createXmlMapper(false).build();
        SimpleModule simpleModule = new SimpleModule();
        simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
        objectMapper.registerModule(simpleModule);
        return objectMapper;
    }
}

```
## 3.分类表格显示优化
#### ①不需要分页，一次查出全部数据
#### ②改为树型表格展示
## 4.分类编辑功能优化
#### 编辑(新增/修改)分类时，支持选中某一分类作为父分类，或无父分类
(父分类改为下拉选择)
```java
<a-form-item label="父分类">
        <a-select
            v-model:value="category.parent"
            ref="select"
        >
          <a-select-option value="0">
            无
          </a-select-option>
          <a-select-option v-for="c in level1" :key="c.id" :value="c.id" :disabled="category.id === c.id">
            {{c.name}}
          </a-select-option>
        </a-select>
</a-form-item>
```
## 5.电子书管理增加分类选择
#### ①分类选择改为级联选择组件
#### ②电子书列表显示分类名称
#### ③ 修复BUG，编辑保存重新加载数据后，再点编辑，则列表显示的还是编辑前的数据
## 6.首页显示分类菜单
## 7.点击分类菜单显示电子书
第八章 用户管理&单点登录

## 1.用户表设计与持久层代码生成
## 2.完成用户表基本增删改查功能
按照电子书管理，复制出一套用户管理的代码
## 3.用户名重复校验与自定义异常
新增用户时，增加用户名重复校验
校验重复时，抛出自定义异常
修改时，用户名不能修改
## 4.关于密码的两层加密处理
密码加密存储
密码加密传输
## 5.增加重置密码功能
修改用户时，不能修改密码
单独开发重置密码表单和接口
## 6.登录功能开发
后端增加登录接口
前端增加登录模态框
## 7.登录成功处理并集成vuex-1
## 8.登录成功处理并集成vuex-2
## 9.增加退出登录功能
## 10.后端接口增加登录校验
## 11.前端页面增加登录校验
## 12.用户密码初始化


// @ts-nocheck：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/162ac70f-01e8-dcc6-f87e-0fe98eee3b0a.png)
项目：04-Vue3集成Vue-Router实现路由跳转

## 一：定义路由
⭐在index.js文件中定义路由：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/7d25e631-76b4-c8ad-192c-ce162deb8419.png)
#### 1.方式一：router-link
```java
<router-link to="/test">通过router-link跳转到测试页面</router-link>
```
#### 2.方法二：a
```java
<a href="/test">通过a标签跳转到测试页面</a>
```
#### 3.方法三：push(跳转后可以返回)
编程式的路由，要导入router对象
```java
<el-button type="primary" @click="router.push('/test')">通过push跳转到新页面</el-button>

import router from '@/router';
```
#### 4.方法四：replace(跳转后无法返回(替换路由))
```java
<el-button type="primary" @click="router.replace('/test')">通过replace跳转到新页面</el-button>

import router from '@/router';
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/9288b6d8-d20e-af2e-8428-a0ea09110a45.png)
## 二：路由传参
query类型的参数
#### 1.传递参数
```java
<el-button type="primary" @click="router.push('/test?id=1')">路由传参id=1</el-button>
```
#### 2.获取参数
多个参数用&连接
在test页面中定义id
```java
id:router.currentRoute.value.query.id,
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/b2506a9c-32c4-0b37-a0ab-9c78692f4ae5.png)
## 三：嵌套路由 RouterView
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/5911343e-5cce-77aa-08c5-56e64aa3ab18.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/d8986613-6863-f0d8-4713-bbcbd073aba1.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/b8d368f7-99d8-1f2b-1613-9bef49a823bc.png)
## 四：路由守卫
在路由配置文件中使用导航守卫，修改网页标题
⭐
to:跳转后route对象(跳转后的一些操作)
from:将要进行跳转的当前route对象(跳转前的一些操作)
next:调用该方法后，才能进入下一个钩子
meta:补充路由参数的对象
beforeEach：表示跳转之前的一些操作
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/1dc85cb7-29d9-dbb0-8fe3-873cdf2e2093.png)
## 五：404页面
#### 1.定义404页面
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/b3ff09b0-775d-c89f-d435-572ac2ec64af.png)

#### 2.定义404路由
```java
{path: '/404', name: '404', meta:{title:'404找不到页面'},component:() => import('../views/404.vue')},

router.beforeEach((to,from,next) => {
   document.title = to.meta.title
   next()  // 必须调用这个函数才能实现跳转
})
```
#### 3.路由输入错误时，统一跳转到404页面
```java
{path: '/:pathMatch(.*)', redirect:'/404'}
```
项目：02-Vue3集成Element-Plus

## 一：Vue3集成Element-Plus
https://element-plus.org/zh-CN/component/overview.html
#### 1.安装依赖
```java
npm install element-plus --save
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/70783b9d-e3b8-f715-e87a-c199b61ecc49.png)
#### 2.在main.js里面导入
```java
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import zhCn from 'element-plus/es/locale/lang/zh-cn'

app.use(ElementPlus,{
    locale:zhCn,
})
```
#### 3.演示按钮组件
⭐color:背景色   style color:文本颜色
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/08dfb858-177a-9034-c4fe-cec9c740ed1d.png)
## 二：Icon图标
#### 1.安装依赖
```java
npm install @element-plus/icons-vue
```
#### 2.在main.js里引入
```java
import * as ElementPlusIconsVue from '@element-plus/icons-vue'

for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
  app.component(key, component)
}
```
#### 3.图标的导入
⭐在按钮中使用图标时，需要单独导入图标![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/007a05e8-fe50-e4f9-16bc-f123be45b7f6.png)

```java
<div style="margin: 30px">
  <el-button type="danger" :icon="Delete" circle />
</div>

import { Delete } from '@element-plus/icons-vue';
```
⭐在输入框中使用图标时，需要单独导入图标![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/ce83598c-963e-cd63-5773-cec1373d4175.png)
```java
<div style="margin: 30px">
      <el-input
      v-model="data.a"
      style="width: 240px"
      placeholder="Type something"
      :prefix-icon="Search"
     />
     
import {Search } from '@element-plus/icons-vue';
</div>
```
#### 4.图标的使用
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/5effc6e2-4360-c797-9488-1edf12cc08ad.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/b9bdb6ee-fa11-a94a-8e37-3d2e14426540.png)
## 三：主题色设置
#### 1.安装依赖
```java 
npm i sass@1.71.1 -D
npm i unplugin-auto-import -D
npm i unplugin-element-plus -D
npm i unplugin-vue-components -D
```
#### 2.配置index.scss
在assets下新建文件index.scss
```java 
@forward "element-plus/theme-chalk/src/common/var.scss" with ($colors: ("primary": ("base": #0066bc),
"success": ("base": #46b61f),
"warning": ("base": #ffad00),
"danger": ("base": #e52f2f),
"info": ("base": #47afdc),
));
```
#### 3.配置vite.config.js
```java
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
import ElementPlus from 'unplugin-element-plus/vite'


plugins: [
    vue(),

    ElementPlus({
      useSource: true,
    }),
    AutoImport({
      resolvers: [ElementPlusResolver({ importStyle: 'sass'})]
    }),
    Components({
      resolvers: [ElementPlusResolver({ importStyle: 'sass'})]
    }),
  ],

  css: {
    preprocessorOptions: {
      scss: {
        additionalData: `
        @use "@/assets/index.scss" as *;`,
      }
    }
  }
```
项目：06-Mysql语法简介

## 数据库基本的增删改查
#### 1.select
①查询所有
```java
select * from 表名;
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/4d6317e9-e395-a4b0-d54f-0823c743c14a.png)
②精确查询(根据字段查询)
```java
select * from employee where name = '汪苏泷';  

```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/b92828bf-549e-597f-16c6-cb66c5537ba1.png)
```java
select * from employee where name = '魏大勋' and age =30;  
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/2554a597-8f88-9b40-f17a-db066e4da451.png)
③模糊查询
```java
// 前缀模糊查询
select * from employee where name like '汪%';  
// 后缀模糊查询
select * from employee where name like '%泷'; 
// 全模糊查询
select * from employee where name like '%泷%'; 
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/8f3fb423-484a-0091-e649-a6176b42adaf.png)
④分页查询
```java
// limit  0代表从第一条数据开始查询 2代表每页显示两条数据
select * from employee limit 0,2;  
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/4c072382-bb4f-97fd-8f94-69255b128536.png)
⑤排序查询
```java
// 降序
select * from employee order by id desc; 
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/0c953ed8-2951-5b05-1e65-c9b8a795fbba.png)
⑥分组查询：一般用于统计个数
```java
// 统计男生人数   group by-- having 组合
select count(*)  from employee group by sex having sex = '男'; 
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/95f0f088-cbc1-9a7c-750d-7b385d7caf17.png)
⑦连接查询-左连接(两个表)
```java
select
  employee.*, 
  dept.name as deptName 
from 
  employee
  left join dept on employee.dept_id = dept.id; 
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/7bbef358-9946-45dc-312a-685169b27afb.png)
#### 2.insert
```java
INSERT INTO `employee` (name,sex,`no`,age,description,dept_id) 
VALUES
	 ('马士兵','男','104',45,'程序员',1);
```
#### 3.update
```java
UPDATE employee
SET name='魏大勋', 
sex='男', 
`no`='102', 
age=30, 
description='演员', 
dept_id=2
WHERE id=2;
```
#### 4.delete
```java
DELETE FROM employee
WHERE id=3;
```
#### 5.清空数据truncate(清空全表)
```java
truncate employee;
```
#### 6.统计数据函数sum 
```java
// 年龄求和
select sum(age) from employee; 
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/20fae06f-1fdf-1419-7f23-24f83e9a7fb1.png)
Azure仮想サーバ上にDifyをデプロイする

# はじめに
昨今話題のDifyだが、導入を検討している方も多いと思う。
月額プランでクラウド利用する方法も一つだが、それと比較し若干？費用が抑えられる、Azure仮想サーバ上に立ち上げる方法について紹介する。
企業内で扱う場合、Azure仮想サーバーは社外ネットワーク上に立ち上がるため、煩わしProxy設定が不要になるメリットがある。
またAzureのクラウド環境が使えるため、セキュリティ面でもクラウド版より安心？かもしれない。

# 簡単な流れ
流れとしては以下の通り。
- Azure仮想サーバを立ち上げる
- 仮想サーバ内でDockerをインストール
- Difyリポジトリをクローン
- 環境変数の設定
- アプリ立ち上げ
- 接続確認

# 手順
## Azure仮想サーバーの立ち上げ
Azure上での仮想サーバー立ち上げ方法については、~~珍しく~~公式ドキュメントがわかりやすいので、以下を紹介する。
https://learn.microsoft.com/ja-jp/azure/virtual-machines/linux/quick-create-portal?tabs=ubuntu

サーバーの最低スペックは、[Dify公式ページ](https://docs.dify.ai/ja-jp/getting-started/install-self-hosted/docker-compose)に以下記載がある。

>Dify インストール前に, マシンが最小インストール要件を満たしていることを確認してください：
> - CPU >= 2 Core
> - RAM >= 4 GiB

OSの種類は、Dockerが動けば何でも良いようだが、Docker Desktopを回避するならLinuxを選択するのが無難。

仮想サーバーが立ち上がれば、SSH接続など設定し、任意のクライアントPCから接続できるようにしておくと楽。

## 仮想サーバー内でDockerインストール
方法は任意。
例えば、以下実行しインストールする。
```bash
$ sudo apt update
$ curl https://get.docker.com | sh
```

## Difyリポジトリをクローン
DifyのGithubリポジトリは以下。
https://github.com/langgenius/dify

仮想サーバー上の任意のディレクトリにクローンする。
```bash
$ git clone https://github.com/langgenius/dify
```

なお、これ以降のアプリ立ち上げ方法については、[公式サイト](https://docs.dify.ai/ja-jp/getting-started/install-self-hosted/docker-compose)の内容と同じ内容。

## 環境変数の設定
`dify/docker`内の`.env.example`を`.env`に反映する。
具体的には以下実行すればOk。
```bash
$ cp .env.example .env
```
`.env`の内容は必要に応じて書き換える。

## アプリ立ち上げ
以下実行し、コンテナを立ち上げる。
```bash
$ docker compose up -d
```
進捗は以下から確認できる。
```bash
$ docker compose ps
```
全てのコンテナが`(healthy)`になれば立ち上げ完了！

## 接続確認
Azure仮想サーバーではパブリックIPアドレスが割り当てられている。
例えば`111.111.111.5`の場合、`http://111.111.111.5`にアクセス可能。
Azure仮想サーバーのパブリックIPアドレスは、Azureポータル上のリソース画面から確認できる。

# 注意点
## クラウドプラントどっちが安い？
正直これは微妙なところで、Azure仮想サーバーのスペックに依存する。
DifyのTeamプランは2025年1月現在、\\$159/monthなのに対し、最低スペックのAzure仮想サーバーでは\\$24/monthとなる。
もちろん最低スペックなので、動作を安定させるにはスペックを上げる他ない。

## Azureがメールサーバー非対応
注意点として、Azure仮想サーバーはメールサーバー非対応であるという点。
AzureではパブリックIPアドレスをプールし、仮想サーバー立ち上げ時にそのIPアドレスを配布する形を取っている（らしい）。
悪用されるリスク低減のため、メールサーバーは全面禁止の模様。
Difyを使ったワークフローの中には、メール機能を使ったものもあるため、それが利用できなくなる。
项目：12-SpringBoot3+Vue3实现文件上传下载功能

## 一：文件上传
```java
// 文件相关的接口
@RestController
@RequestMapping("/files")
public class FileController {
    // System.getProperty("user.dir")： 获取到当前项目的根路径
    // 文件上传的目录的路径
    private static final String filePath = System.getProperty("user.dir") + "/files/";
    // 文件上传接口
    @PostMapping("/upload")
    public Result upload(MultipartFile file){  // 文件流的形式接收前端发送过来的文件
        String originalFilename = file.getOriginalFilename();
        if(!FileUtil.isDirectory(filePath)){ // 如果目录不存在需要先创建目录
            FileUtil.mkdir(filePath);// 创建一个files目录
        }
        // 提供文件存储的完整路径
        // 给文件名加一个唯一的标识
        String fileName = System.currentTimeMillis() + "_" + originalFilename;
        String realPath = filePath + fileName; // 完整的文件路径
        try{
            FileUtil.writeBytes(file.getBytes(),realPath);
        } catch(Exception e){
            e.printStackTrace();
            throw new CustomException("500","文件上传失败");
        }
        // 返回一个网络链接
        String url = "http://localhost:8080/files/download/" + fileName;
        return Result.success(url);
    }

    // 文件下载
    @GetMapping("/download/{fileName}")
    public void download(@PathVariable String fileName, HttpServletResponse response) throws IOException {
        response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
        response.setContentType("application/octet-stream");
        OutputStream os = response.getOutputStream();
        String realPath = filePath + fileName;
        // 完整的文件路径 http://localhost:8080/files/download/1734310352785_1.jpeg
        // 获取到文件的字节数组
        byte[] bytes = FileUtil.readBytes(realPath);
        os.write(bytes);
        os.flush();
        os.close();
    }
}
```
## 二：文件下载
```java
response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
response.setContentType("application/octet-stream");
```
## 三：前端对接文件上传和下载
#### 1.用户头像
⭐upload组件
```java
<div style="width: 100%;display: flex;justify-content: center;margin-bottom: 20px">
    <el-upload 
     class="avatar-uploader" 
     action="http://localhost:8080/files/upload" 
     :show-file-list="false"
     :on-success="handleAvatarSuccess">
     <img v-if="data.form.avatar" :src="data.form.avatar" class="avatar" />
     <el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
    </el-upload>
</div>
```
⭐回调函数
```java
const handleAvatarSuccess = (res) => {
  data.form.avatar = res.data
}
```
## 四：表格中显示头像
```java
<el-table-column label="头像">
  <template #default="scope">
    <img v-if="scope.row.avatar" :src="scope.row.avatar"  alt="" style="display: block;width: 40px;height: 40px;border-radius: 50%" />
  </template>
</el-table-column>
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/f95abfe7-30b6-28d2-76eb-0b723b341cb7.png)
## 五：上传头像表单
```java
<el-form-item label="头像">
  <el-upload action="http://localhost:8080/files/upload" list-type="picture" :on-success="handleAvatarSuccess">
    <el-button type="primary">上传头像</el-button>
  </el-upload>
</el-form-item>

const handleAvatarSuccess = (res) => {
  data.form.avatar = res.data
}
```