diff --git a/Makefile b/Makefile
index 785dc15..6c7922a 100644
--- a/Makefile
+++ b/Makefile
@@ -18,11 +18,11 @@ test: # 進行測試
 fmt: # 格式優化
 	$(GOFMT) -w $(GOFILES)
 	goimports -w  ./
+	golangci-lint run
 
 .PHONY: gen-rpc
 gen-rpc: # 建立 rpc code
 	$(GO_CTL_NAME) rpc protoc ./generate/protobuf/notification.proto -m --style=$(GO_ZERO_STYLE) --go_out=./gen_result/pb --go-grpc_out=./gen_result/pb --zrpc_out=.
-	copy ./etc/service.yaml ./etc/service.example.yaml
 	go mod tidy
 	@echo "Generate core-api files successfully"
 
@@ -44,6 +44,6 @@ run-docker: # 建立 rpc code
 .PHONY: build-docker
 build-docker:
 	cp ./build/Dockerfile Dockerfile
-	docker buildx build -t $(DOCKER_REPO):$(VERSION) --build-arg SSH_PRIVATE_KEY="$(cat ~/.ssh/ed_25519)" .
+	docker buildx build -t $(DOCKER_REPO):$(VERSION) --build-arg SSH_PRIVATE_KEY="$(cat ~/.ssh/id_ed25519)" .
 	rm -rf Dockerfile
 	@echo "Generate core-api files successfully"
diff --git a/build/Dockerfile b/build/Dockerfile
index c5e8f4b..38ea0e9 100644
--- a/build/Dockerfile
+++ b/build/Dockerfile
@@ -2,7 +2,7 @@
 # BUILDER #
 ###########
 
-FROM golang:1.22.3 as builder
+FROM golang:1.24.0 as builder
 
 ARG VERSION
 ARG BUILT
@@ -41,7 +41,7 @@ RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \
 FROM gcr.io/distroless/static-debian11
 WORKDIR /app
 
-COPY --from=builder /app/service /app/service
-COPY --from=builder /app/etc/service.yaml /app/etc/service.yaml
+COPY --from=builder /app /app
+COPY --from=builder /app/etc/notification.yaml /app/etc/notification.yaml
 EXPOSE 8080
 CMD ["/app/notification"]
\ No newline at end of file
diff --git a/client/senderservice/sender_service.go b/client/senderservice/sender_service.go
index bbc3d18..dc7ba3f 100644
--- a/client/senderservice/sender_service.go
+++ b/client/senderservice/sender_service.go
@@ -1,4 +1,5 @@
 // Code generated by goctl. DO NOT EDIT.
+// goctl 1.7.3
 // Source: notification.proto
 
 package senderservice
@@ -13,21 +14,20 @@ import (
 )
 
 type (
-	NoneReq             = notification.NoneReq
-	OKResp              = notification.OKResp
-	SendByTemplateIDReq = notification.SendByTemplateIDReq
-	SendMailReq         = notification.SendMailReq
-	SendSMSReq          = notification.SendSMSReq
+	NoneReq      = notification.NoneReq
+	OKResp       = notification.OKResp
+	SendMailReq  = notification.SendMailReq
+	SendSMSReq   = notification.SendSMSReq
+	TemplateReq  = notification.TemplateReq
+	TemplateResp = notification.TemplateResp
 
 	SenderService interface {
 		// SendMail 寄信
 		SendMail(ctx context.Context, in *SendMailReq, opts ...grpc.CallOption) (*OKResp, error)
 		// SendSms 寄簡訊
 		SendSms(ctx context.Context, in *SendSMSReq, opts ...grpc.CallOption) (*OKResp, error)
-		// SendMailByTemplateId 寄送模板信件
-		SendMailByTemplateId(ctx context.Context, in *SendByTemplateIDReq, opts ...grpc.CallOption) (*OKResp, error)
-		// SendSmsByTemplateId 寄送模板簡訊
-		SendSmsByTemplateId(ctx context.Context, in *SendByTemplateIDReq, opts ...grpc.CallOption) (*OKResp, error)
+		// 取得 Template
+		GetStaticTemplate(ctx context.Context, in *TemplateReq, opts ...grpc.CallOption) (*TemplateResp, error)
 	}
 
 	defaultSenderService struct {
@@ -53,14 +53,8 @@ func (m *defaultSenderService) SendSms(ctx context.Context, in *SendSMSReq, opts
 	return client.SendSms(ctx, in, opts...)
 }
 
-// SendMailByTemplateId 寄送模板信件
-func (m *defaultSenderService) SendMailByTemplateId(ctx context.Context, in *SendByTemplateIDReq, opts ...grpc.CallOption) (*OKResp, error) {
+// 取得 Template
+func (m *defaultSenderService) GetStaticTemplate(ctx context.Context, in *TemplateReq, opts ...grpc.CallOption) (*TemplateResp, error) {
 	client := notification.NewSenderServiceClient(m.cli.Conn())
-	return client.SendMailByTemplateId(ctx, in, opts...)
-}
-
-// SendSmsByTemplateId 寄送模板簡訊
-func (m *defaultSenderService) SendSmsByTemplateId(ctx context.Context, in *SendByTemplateIDReq, opts ...grpc.CallOption) (*OKResp, error) {
-	client := notification.NewSenderServiceClient(m.cli.Conn())
-	return client.SendSmsByTemplateId(ctx, in, opts...)
+	return client.GetStaticTemplate(ctx, in, opts...)
 }
diff --git a/etc/service.example.yaml b/etc/service.example.yaml
index 07047e8..7a1edeb 100644
--- a/etc/service.example.yaml
+++ b/etc/service.example.yaml
@@ -4,13 +4,31 @@ Etcd:
   Hosts:
     - 127.0.0.1:2379
   Key: notification.rpc
-SMTP:
-  Host: smtp.mail.host
-  Port: 25
-  User: smtp@user.net
-  Password: smtp_password
 
-SMSSender:
-  User: sms@user.net
-  Password : sms_password
+SMTPConfig:
+  Enable: false
+  Sort: 1
+  GoroutinePoolNum: 10
+  Host: xxxxxx
+  Port: xxxxxx
+  Username: xxxxxx
+  Password: xxxxxx
+
+AmazonSesSettings:
+  Enable: false
+  Sort: 2
+  PoolSize: 2000
+  Region   : ap-northeast-3
+  Sender    : xxxxxx
+  Charset   : xxxxxx
+  AccessKey : xxxxxx
+  SecretKey : xxxxxx
+  Token     : xxxxxx
+
+MitakeSMSSender:
+  Enable: false
+  Sort: 1
+  PoolSize: 10
+  User: xxxxxx
+  Password : xxxxxx
 
diff --git a/generate/database/mongodb/readme.md b/generate/database/mongodb/readme.md
deleted file mode 100644
index 5cbb27e..0000000
--- a/generate/database/mongodb/readme.md
+++ /dev/null
@@ -1 +0,0 @@
-如果有需要可以把 mongo 放這邊
\ No newline at end of file
diff --git a/generate/database/mysql/20240816014305_create_permission_table.down.sql b/generate/database/mysql/20240816014305_create_permission_table.down.sql
deleted file mode 100644
index 6dc8750..0000000
--- a/generate/database/mysql/20240816014305_create_permission_table.down.sql
+++ /dev/null
@@ -1 +0,0 @@
-DROP TABLE IF EXISTS `permission`;
diff --git a/generate/database/mysql/20240816014305_create_permission_table.up.sql b/generate/database/mysql/20240816014305_create_permission_table.up.sql
deleted file mode 100644
index cbe4d31..0000000
--- a/generate/database/mysql/20240816014305_create_permission_table.up.sql
+++ /dev/null
@@ -1,15 +0,0 @@
--- 通常會把整個表都放到記憶體當中,不常搜尋,不需要加其他搜尋的 index
-CREATE TABLE `permission`
-(
-    `id`          bigint unsigned           NOT NULL AUTO_INCREMENT COMMENT 'PK',
-    `parent`      bigint unsigned DEFAULT NULL,
-    `name`        varchar(255)              NOT NULL,
-    `http_method` varchar(255)              NOT NULL,
-    `http_path`   text                      NOT NULL,
-    `status` tinyint NOT NULL DEFAULT '1' COMMENT '狀態 1: 啟用, 2: 關閉',
-    `type` tinyint NOT NULL DEFAULT '1' COMMENT '狀態 1: 後台, 2: 前台',
-    `create_time` bigint          DEFAULT 0 NOT NULL COMMENT '創建時間',
-    `update_time` bigint          DEFAULT 0 NOT NULL COMMENT '更新時間',
-    PRIMARY KEY (`id`),
-    UNIQUE KEY `name_unique_key` (`name`)
-) ENGINE = InnoDB COMMENT ='權限表';
\ No newline at end of file
diff --git a/generate/database/mysql/create/20230529020000_create_schema.down.sql b/generate/database/mysql/create/20230529020000_create_schema.down.sql
deleted file mode 100644
index 766ea05..0000000
--- a/generate/database/mysql/create/20230529020000_create_schema.down.sql
+++ /dev/null
@@ -1 +0,0 @@
-DROP DATABASE IF EXISTS `example`;
\ No newline at end of file
diff --git a/generate/database/mysql/create/20230529020000_create_schema.up.sql b/generate/database/mysql/create/20230529020000_create_schema.up.sql
deleted file mode 100644
index 07648b3..0000000
--- a/generate/database/mysql/create/20230529020000_create_schema.up.sql
+++ /dev/null
@@ -1 +0,0 @@
-CREATE DATABASE IF NOT EXISTS `example`;
\ No newline at end of file
diff --git a/generate/database/readme.md b/generate/database/readme.md
deleted file mode 100644
index 4db6e02..0000000
--- a/generate/database/readme.md
+++ /dev/null
@@ -1,39 +0,0 @@
-# migrate
-數據庫遷移工具
-
-[golang-migrate](https://github.com/golang-migrate/migrate)
-
-## 安裝 make
-```shell
-brew install Makefile
-```
-
-## 安裝 golang-migrate
-```shell
-brew install golang-migrate
-```
-
-## 執行刪除 mysql schema
-
-```shell
-migrate -source file://database/migrations/mysql -database 'mysql://account:password@tcp(127.0.0.1:3306)/esc_c2c' down
-```
-
-## 執行安裝 mysql schema
-
-```shell
-migrate -source file://database/migrations/mysql -database 'mysql://account:password@tcp(127.0.0.1:3306)/esc_c2c' up
-```
-
-## 執行刪除 mongo schema
-
-```shell
-migrate -source file://database/migrations/mongodb -database 'mongodb://127.0.0.1:27017/esc_c2c' down
-```
-
-## 執行安裝 mongo schema
-
-```shell
-migrate -source file://database/migrations/mongodb -database 'mongodb://127.0.0.1:27017/esc_c2c' up
-```
-
diff --git a/generate/database/seeder/20230620025708_init_role.down.sql b/generate/database/seeder/20230620025708_init_role.down.sql
deleted file mode 100644
index 36a1962..0000000
--- a/generate/database/seeder/20230620025708_init_role.down.sql
+++ /dev/null
@@ -1 +0,0 @@
-DELETE FROM `role` WHERE (`role_id` = 'AM000000');
diff --git a/generate/database/seeder/20230620025708_init_role.up.sql b/generate/database/seeder/20230620025708_init_role.up.sql
deleted file mode 100644
index d4c158e..0000000
--- a/generate/database/seeder/20230620025708_init_role.up.sql
+++ /dev/null
@@ -1,3 +0,0 @@
-INSERT INTO `role` (`role_id`, `display_name`, `status`, `create_time`, `update_time`)
-VALUES ('AM000000', 'admin', 1, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()),
-       ('AM000001', 'visitor', 1, UNIX_TIMESTAMP(), UNIX_TIMESTAMP());
\ No newline at end of file
diff --git a/generate/protobuf/notification.proto b/generate/protobuf/notification.proto
index e3d5304..25991f3 100644
--- a/generate/protobuf/notification.proto
+++ b/generate/protobuf/notification.proto
@@ -22,11 +22,14 @@ message SendSMSReq {
   string recipient_name=3;
 }
 
-message SendByTemplateIDReq {
-  string to = 1;
-  string template_id =2;
-  string lang=3;
-  map<string, string> content_data = 4;
+message TemplateReq {
+  string language = 1;
+  string template_id = 2;
+}
+
+message TemplateResp {
+  string title = 1;
+  string body = 2;
 }
 
 
@@ -35,10 +38,8 @@ service SenderService {
   rpc SendMail(SendMailReq) returns(OKResp);
   // SendSms 寄簡訊
   rpc SendSms(SendSMSReq) returns(OKResp);
-  // SendMailByTemplateId 寄送模板信件
-  rpc SendMailByTemplateId(SendByTemplateIDReq) returns(OKResp);
-  // SendSmsByTemplateId 寄送模板簡訊
-  rpc SendSmsByTemplateId(SendByTemplateIDReq) returns(OKResp);
+  // 取得 Template
+  rpc GetStaticTemplate(TemplateReq) returns(TemplateResp);
 }
 
 
diff --git a/go.mod b/go.mod
index 9ea4180..8e3fe52 100644
--- a/go.mod
+++ b/go.mod
@@ -1,11 +1,14 @@
 module app-cloudep-notification-service
 
-go 1.22.3
+go 1.24.0
 
 require (
-	code.30cm.net/digimon/library-go/errs v1.2.3
-	code.30cm.net/digimon/library-go/validator v1.0.0
+	code.30cm.net/digimon/library-go/errs v1.2.14
 	code.30cm.net/digimon/library-go/worker_pool v0.0.0-20240820153352-f9c90a90f5e2
+	github.com/aws/aws-sdk-go-v2 v1.36.3
+	github.com/aws/aws-sdk-go-v2/credentials v1.17.61
+	github.com/aws/aws-sdk-go-v2/service/ses v1.30.0
+	github.com/matcornic/hermes/v2 v2.1.0
 	github.com/minchao/go-mitake v1.0.0
 	github.com/zeromicro/go-zero v1.7.0
 	google.golang.org/grpc v1.65.0
@@ -14,6 +17,14 @@ require (
 )
 
 require (
+	github.com/Masterminds/semver v1.4.2 // indirect
+	github.com/Masterminds/sprig v2.16.0+incompatible // indirect
+	github.com/PuerkitoBio/goquery v1.5.0 // indirect
+	github.com/andybalholm/cascadia v1.0.0 // indirect
+	github.com/aokoli/goutils v1.0.1 // indirect
+	github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 // indirect
+	github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 // indirect
+	github.com/aws/smithy-go v1.22.2 // indirect
 	github.com/beorn7/perks v1.0.1 // indirect
 	github.com/cenkalti/backoff/v4 v4.3.0 // indirect
 	github.com/cespare/xxhash/v2 v2.3.0 // indirect
@@ -23,15 +34,11 @@ require (
 	github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
 	github.com/emicklei/go-restful/v3 v3.11.0 // indirect
 	github.com/fatih/color v1.17.0 // indirect
-	github.com/gabriel-vasile/mimetype v1.4.3 // indirect
 	github.com/go-logr/logr v1.4.2 // indirect
 	github.com/go-logr/stdr v1.2.2 // indirect
 	github.com/go-openapi/jsonpointer v0.19.6 // indirect
 	github.com/go-openapi/jsonreference v0.20.2 // indirect
 	github.com/go-openapi/swag v0.22.4 // indirect
-	github.com/go-playground/locales v0.14.1 // indirect
-	github.com/go-playground/universal-translator v0.18.1 // indirect
-	github.com/go-playground/validator/v10 v10.22.0 // indirect
 	github.com/gogo/protobuf v1.3.2 // indirect
 	github.com/golang/mock v1.6.0 // indirect
 	github.com/golang/protobuf v1.5.4 // indirect
@@ -39,16 +46,21 @@ require (
 	github.com/google/go-cmp v0.6.0 // indirect
 	github.com/google/gofuzz v1.2.0 // indirect
 	github.com/google/uuid v1.6.0 // indirect
+	github.com/gorilla/css v1.0.0 // indirect
 	github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
+	github.com/huandu/xstrings v1.2.0 // indirect
+	github.com/imdario/mergo v0.3.6 // indirect
+	github.com/jaytaylor/html2text v0.0.0-20180606194806-57d518f124b0 // indirect
 	github.com/josharian/intern v1.0.0 // indirect
 	github.com/json-iterator/go v1.1.12 // indirect
-	github.com/leodido/go-urn v1.4.0 // indirect
 	github.com/mailru/easyjson v0.7.7 // indirect
 	github.com/mattn/go-colorable v0.1.13 // indirect
 	github.com/mattn/go-isatty v0.0.20 // indirect
+	github.com/mattn/go-runewidth v0.0.15 // indirect
 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
 	github.com/modern-go/reflect2 v1.0.2 // indirect
 	github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
+	github.com/olekukonko/tablewriter v0.0.5 // indirect
 	github.com/openzipkin/zipkin-go v0.4.3 // indirect
 	github.com/panjf2000/ants/v2 v2.10.0 // indirect
 	github.com/pelletier/go-toml/v2 v2.2.2 // indirect
@@ -57,7 +69,13 @@ require (
 	github.com/prometheus/common v0.48.0 // indirect
 	github.com/prometheus/procfs v0.12.0 // indirect
 	github.com/redis/go-redis/v9 v9.6.1 // indirect
+	github.com/rivo/uniseg v0.2.0 // indirect
+	github.com/russross/blackfriday/v2 v2.0.1 // indirect
+	github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
 	github.com/spaolacci/murmur3 v1.1.0 // indirect
+	github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
+	github.com/vanng822/css v0.0.0-20190504095207-a21e860bcd04 // indirect
+	github.com/vanng822/go-premailer v0.0.0-20191214114701-be27abe028fe // indirect
 	go.etcd.io/etcd/api/v3 v3.5.15 // indirect
 	go.etcd.io/etcd/client/pkg/v3 v3.5.15 // indirect
 	go.etcd.io/etcd/client/v3 v3.5.15 // indirect
diff --git a/internal/config/config.go b/internal/config/config.go
index 3521805..65212ac 100755
--- a/internal/config/config.go
+++ b/internal/config/config.go
@@ -5,14 +5,34 @@ import "github.com/zeromicro/go-zero/zrpc"
 type Config struct {
 	zrpc.RpcServerConf
 
-	SMTP struct {
+	SMTPConfig struct {
+		Enable           bool
+		Sort             int
+		GoroutinePoolNum int
+
 		Host     string
 		Port     int
-		User     string
+		Username string
 		Password string
 	}
 
-	SMSSender struct {
+	AmazonSesSettings struct {
+		Enable   bool
+		Sort     int
+		PoolSize int
+
+		Region    string
+		Sender    string
+		Charset   string
+		AccessKey string
+		SecretKey string
+		Token     string
+	}
+	MitakeSMSSender struct {
+		Enable   bool
+		Sort     int
+		PoolSize int
+
 		User     string
 		Password string
 	}
diff --git a/internal/domain/errors.go b/internal/domain/errors.go
deleted file mode 100644
index b6d54f0..0000000
--- a/internal/domain/errors.go
+++ /dev/null
@@ -1,58 +0,0 @@
-package domain
-
-import (
-	"fmt"
-	"strings"
-
-	"code.30cm.net/digimon/library-go/errs"
-	"code.30cm.net/digimon/library-go/errs/code"
-	"github.com/zeromicro/go-zero/core/logx"
-)
-
-type ErrorCode uint32
-
-func (e ErrorCode) ToUint32() uint32 {
-	return uint32(e)
-}
-
-const (
-	_                           = iota
-	SendMailErrorCode ErrorCode = iota
-	SendSMSErrorCode
-)
-
-// SendMailError ...
-func SendMailError(s ...string) *errs.LibError {
-	return errs.NewError(code.CloudEPNotification, code.ThirdParty,
-		SendMailErrorCode.ToUint32(),
-		fmt.Sprintf("%s", strings.Join(s, " ")))
-}
-
-// SendMailErrorL logs error message and returns Err
-func SendMailErrorL(l logx.Logger, filed []logx.LogField, s ...string) *errs.LibError {
-	e := SendMailError(s...)
-	if filed != nil || len(filed) >= 0 {
-		l.WithCallerSkip(1).WithFields(filed...).Error(e.Error())
-	}
-	l.WithCallerSkip(1).Error(e.Error())
-
-	return e
-}
-
-// SendSMSError ...
-func SendSMSError(s ...string) *errs.LibError {
-	return errs.NewError(code.CloudEPNotification, code.ThirdParty,
-		SendSMSErrorCode.ToUint32(),
-		fmt.Sprintf("%s", strings.Join(s, " ")))
-}
-
-// SendSMSErrorL logs error message and returns Err
-func SendSMSErrorL(l logx.Logger, filed []logx.LogField, s ...string) *errs.LibError {
-	e := SendSMSError(s...)
-	if filed != nil || len(filed) >= 0 {
-		l.WithCallerSkip(1).WithFields(filed...).Error(e.Error())
-	}
-	l.WithCallerSkip(1).Error(e.Error())
-
-	return e
-}
diff --git a/internal/domain/usecase/mail.go b/internal/domain/usecase/mail.go
deleted file mode 100644
index 95fb400..0000000
--- a/internal/domain/usecase/mail.go
+++ /dev/null
@@ -1,18 +0,0 @@
-package usecase
-
-import "context"
-
-type MailClientUseCase interface {
-	SendMail(ctx context.Context, req MailReq) error
-}
-
-type MailReq struct {
-	To      string
-	From    string
-	Subject string
-	Body    string
-}
-
-type MailUseCase interface {
-	GetMailTemplateByID(ctx context.Context, tid int64) ([]rune, error)
-}
diff --git a/internal/domain/usecase/sms.go b/internal/domain/usecase/sms.go
deleted file mode 100644
index c4e6d66..0000000
--- a/internal/domain/usecase/sms.go
+++ /dev/null
@@ -1,18 +0,0 @@
-package usecase
-
-import (
-	"context"
-)
-
-type SMSClientUseCase interface {
-	SendSMS(ctx context.Context, req SMSReq) error
-}
-
-type SMSReq struct {
-	// RecipientAddress 接收者號碼
-	RecipientAddress string
-	// RecipientName 接收者姓名
-	RecipientName string
-	// Body 要傳送的訊息
-	Body string
-}
diff --git a/internal/logic/senderservice/get_static_template_logic.go b/internal/logic/senderservice/get_static_template_logic.go
new file mode 100644
index 0000000..fecce88
--- /dev/null
+++ b/internal/logic/senderservice/get_static_template_logic.go
@@ -0,0 +1,42 @@
+package senderservicelogic
+
+import (
+	"app-cloudep-notification-service/pkg/domain/template"
+	"context"
+
+	"app-cloudep-notification-service/gen_result/pb/notification"
+	"app-cloudep-notification-service/internal/svc"
+
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type GetStaticTemplateLogic struct {
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+	logx.Logger
+}
+
+func NewGetStaticTemplateLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetStaticTemplateLogic {
+	return &GetStaticTemplateLogic{
+		ctx:    ctx,
+		svcCtx: svcCtx,
+		Logger: logx.WithContext(ctx),
+	}
+}
+
+// GetStaticTemplate 取得 Template
+func (l *GetStaticTemplateLogic) GetStaticTemplate(in *notification.TemplateReq) (*notification.TemplateResp, error) {
+	tmp, err := l.svcCtx.TemplateUseCase.GetEmailTemplateByStatic(
+		l.ctx,
+		template.Language(in.GetLanguage()),
+		template.Type(in.GetTemplateId()),
+	)
+	if err != nil {
+		return nil, err
+	}
+
+	return &notification.TemplateResp{
+		Title: tmp.Title,
+		Body:  tmp.Body,
+	}, nil
+}
diff --git a/internal/logic/senderservice/send_mail_by_template_id_logic.go b/internal/logic/senderservice/send_mail_by_template_id_logic.go
deleted file mode 100644
index 08f53ae..0000000
--- a/internal/logic/senderservice/send_mail_by_template_id_logic.go
+++ /dev/null
@@ -1,29 +0,0 @@
-package senderservicelogic
-
-import (
-	"app-cloudep-notification-service/gen_result/pb/notification"
-	"app-cloudep-notification-service/internal/svc"
-	"context"
-
-	"github.com/zeromicro/go-zero/core/logx"
-)
-
-type SendMailByTemplateIdLogic struct {
-	ctx    context.Context
-	svcCtx *svc.ServiceContext
-	logx.Logger
-}
-
-func NewSendMailByTemplateIdLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SendMailByTemplateIdLogic {
-	return &SendMailByTemplateIdLogic{
-		ctx:    ctx,
-		svcCtx: svcCtx,
-		Logger: logx.WithContext(ctx),
-	}
-}
-
-// SendMailByTemplateId 寄送模板信件
-func (l *SendMailByTemplateIdLogic) SendMailByTemplateId(in *notification.SendByTemplateIDReq) (*notification.OKResp, error) {
-
-	return &notification.OKResp{}, nil
-}
diff --git a/internal/logic/senderservice/send_mail_logic.go b/internal/logic/senderservice/send_mail_logic.go
index 7b16399..e2b1b7b 100644
--- a/internal/logic/senderservice/send_mail_logic.go
+++ b/internal/logic/senderservice/send_mail_logic.go
@@ -1,12 +1,9 @@
 package senderservicelogic
 
 import (
-	"app-cloudep-notification-service/internal/domain"
-	"app-cloudep-notification-service/internal/domain/usecase"
+	"app-cloudep-notification-service/pkg/domain/usecase"
 	"context"
 
-	ers "code.30cm.net/digimon/library-go/errs"
-
 	"app-cloudep-notification-service/gen_result/pb/notification"
 	"app-cloudep-notification-service/internal/svc"
 
@@ -27,45 +24,16 @@ func NewSendMailLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SendMail
 	}
 }
 
-type sendMailReq struct {
-	// TO 收件者
-	To string `validate:"required,email"`
-	// Subject 信件主旨
-	Subject string `validate:"required,max=128"`
-	// Body 內容
-	Body string `validate:"required"`
-	// From 寄件者
-	From string `validate:"required"`
-}
-
 // SendMail 寄信
 func (l *SendMailLogic) SendMail(in *notification.SendMailReq) (*notification.OKResp, error) {
-	if err := l.svcCtx.Validate.ValidateAll(&sendMailReq{
-		To:      in.GetTo(),
+	err := l.svcCtx.DeliveryUseCase.SendEmail(l.ctx, usecase.MailReq{
+		To:      []string{in.GetTo()},
 		Subject: in.GetSubject(),
 		Body:    in.GetBody(),
 		From:    in.GetFrom(),
-	}); err != nil {
-		return nil, ers.InvalidFormat("invalid format")
-	}
-
-	// TODO 以後可以做換線
-	err := l.svcCtx.MailSender.SendMail(l.ctx, usecase.MailReq{
-		To:      in.GetTo(),
-		Subject: in.GetSubject(),
-		From:    in.GetFrom(),
-		Body:    in.GetBody(),
 	})
 	if err != nil {
-		return nil, domain.SendMailErrorL(
-			logx.WithContext(l.ctx),
-			[]logx.LogField{
-				logx.Field("func", "MailSender.SendMail"),
-				logx.Field("in", in),
-				logx.Field("err", err),
-			},
-			"MailSender.SendMail failed to send mail",
-		)
+		return nil, err
 	}
 
 	return &notification.OKResp{}, nil
diff --git a/internal/logic/senderservice/send_sms_by_template_id_logic.go b/internal/logic/senderservice/send_sms_by_template_id_logic.go
deleted file mode 100644
index 0ec19d2..0000000
--- a/internal/logic/senderservice/send_sms_by_template_id_logic.go
+++ /dev/null
@@ -1,31 +0,0 @@
-package senderservicelogic
-
-import (
-	"context"
-
-	"app-cloudep-notification-service/gen_result/pb/notification"
-	"app-cloudep-notification-service/internal/svc"
-
-	"github.com/zeromicro/go-zero/core/logx"
-)
-
-type SendSmsByTemplateIdLogic struct {
-	ctx    context.Context
-	svcCtx *svc.ServiceContext
-	logx.Logger
-}
-
-func NewSendSmsByTemplateIdLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SendSmsByTemplateIdLogic {
-	return &SendSmsByTemplateIdLogic{
-		ctx:    ctx,
-		svcCtx: svcCtx,
-		Logger: logx.WithContext(ctx),
-	}
-}
-
-// SendSmsByTemplateId 寄送模板簡訊
-func (l *SendSmsByTemplateIdLogic) SendSmsByTemplateId(in *notification.SendByTemplateIDReq) (*notification.OKResp, error) {
-	// todo: add your logic here and delete this line
-
-	return &notification.OKResp{}, nil
-}
diff --git a/internal/logic/senderservice/send_sms_logic.go b/internal/logic/senderservice/send_sms_logic.go
index 28e30a9..85b3ff6 100644
--- a/internal/logic/senderservice/send_sms_logic.go
+++ b/internal/logic/senderservice/send_sms_logic.go
@@ -1,12 +1,9 @@
 package senderservicelogic
 
 import (
-	"app-cloudep-notification-service/internal/domain"
-	"app-cloudep-notification-service/internal/domain/usecase"
+	"app-cloudep-notification-service/pkg/domain/usecase"
 	"context"
 
-	"code.30cm.net/digimon/library-go/errs"
-
 	"app-cloudep-notification-service/gen_result/pb/notification"
 	"app-cloudep-notification-service/internal/svc"
 
@@ -27,41 +24,15 @@ func NewSendSmsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SendSmsLo
 	}
 }
 
-type sendSMSReq struct {
-	// RecipientAddress 收件者
-	RecipientAddress string `validate:"required,phone"`
-	// Body 內容
-	Body string `validate:"required"`
-	// RecipientName 收件者信名
-	RecipientName string `validate:"required"`
-}
-
 // SendSms 寄簡訊
 func (l *SendSmsLogic) SendSms(in *notification.SendSMSReq) (*notification.OKResp, error) {
-	if err := l.svcCtx.Validate.ValidateAll(&sendSMSReq{
-		RecipientName:    in.GetTo(),
-		Body:             in.GetBody(),
-		RecipientAddress: in.GetTo(),
-	}); err != nil {
-		return nil, errs.InvalidFormat(err.Error())
-	}
-
-	// TODO 以後可以做換線
-	err := l.svcCtx.SMSSender.SendSMS(l.ctx, usecase.SMSReq{
-		RecipientAddress: in.GetTo(),
-		RecipientName:    in.GetRecipientName(),
-		Body:             in.GetBody(),
+	err := l.svcCtx.DeliveryUseCase.SendMessage(l.ctx, usecase.SMSMessageRequest{
+		PhoneNumber:    in.GetTo(),
+		RecipientName:  in.GetRecipientName(),
+		MessageContent: in.GetBody(),
 	})
 	if err != nil {
-		return nil, domain.SendSMSErrorL(
-			logx.WithContext(l.ctx),
-			[]logx.LogField{
-				logx.Field("func", "SMSSender.SendSMS"),
-				logx.Field("in", in),
-				logx.Field("err", err),
-			},
-			"SMSSender.SendSMS failed to send sms",
-		)
+		return nil, err
 	}
 
 	return &notification.OKResp{}, nil
diff --git a/internal/server/senderservice/sender_service_server.go b/internal/server/senderservice/sender_service_server.go
index 6c1ead4..f669d83 100644
--- a/internal/server/senderservice/sender_service_server.go
+++ b/internal/server/senderservice/sender_service_server.go
@@ -1,4 +1,5 @@
 // Code generated by goctl. DO NOT EDIT.
+// goctl 1.7.3
 // Source: notification.proto
 
 package server
@@ -8,7 +9,6 @@ import (
 
 	"app-cloudep-notification-service/gen_result/pb/notification"
 	senderservicelogic "app-cloudep-notification-service/internal/logic/senderservice"
-
 	"app-cloudep-notification-service/internal/svc"
 )
 
@@ -35,14 +35,8 @@ func (s *SenderServiceServer) SendSms(ctx context.Context, in *notification.Send
 	return l.SendSms(in)
 }
 
-// SendMailByTemplateId 寄送模板信件
-func (s *SenderServiceServer) SendMailByTemplateId(ctx context.Context, in *notification.SendByTemplateIDReq) (*notification.OKResp, error) {
-	l := senderservicelogic.NewSendMailByTemplateIdLogic(ctx, s.svcCtx)
-	return l.SendMailByTemplateId(in)
-}
-
-// SendSmsByTemplateId 寄送模板簡訊
-func (s *SenderServiceServer) SendSmsByTemplateId(ctx context.Context, in *notification.SendByTemplateIDReq) (*notification.OKResp, error) {
-	l := senderservicelogic.NewSendSmsByTemplateIdLogic(ctx, s.svcCtx)
-	return l.SendSmsByTemplateId(in)
+// 取得 Template
+func (s *SenderServiceServer) GetStaticTemplate(ctx context.Context, in *notification.TemplateReq) (*notification.TemplateResp, error) {
+	l := senderservicelogic.NewGetStaticTemplateLogic(ctx, s.svcCtx)
+	return l.GetStaticTemplate(in)
 }
diff --git a/internal/svc/service_context.go b/internal/svc/service_context.go
index d5a59dc..746d849 100644
--- a/internal/svc/service_context.go
+++ b/internal/svc/service_context.go
@@ -2,29 +2,85 @@ package svc
 
 import (
 	"app-cloudep-notification-service/internal/config"
-	domainUC "app-cloudep-notification-service/internal/domain/usecase"
-	"app-cloudep-notification-service/internal/usecase"
+	cfg "app-cloudep-notification-service/pkg/config"
+	useD "app-cloudep-notification-service/pkg/domain/usecase"
+	"app-cloudep-notification-service/pkg/repository"
+	"app-cloudep-notification-service/pkg/usecase"
 
 	"code.30cm.net/digimon/library-go/errs"
 	"code.30cm.net/digimon/library-go/errs/code"
-
-	v "code.30cm.net/digimon/library-go/validator"
 )
 
 type ServiceContext struct {
-	Config config.Config
-
-	Validate   v.Validate
-	MailSender domainUC.MailClientUseCase
-	SMSSender  domainUC.SMSClientUseCase
+	Config          config.Config
+	DeliveryUseCase useD.DeliveryUseCase
+	TemplateUseCase useD.TemplateUseCase
 }
 
 func NewServiceContext(c config.Config) *ServiceContext {
 	errs.Scope = code.CloudEPNotification
+
+	param := usecase.DeliveryUseCaseParam{}
+	if c.AmazonSesSettings.Enable {
+		sesRepo := repository.MustAwsSesMailRepository(repository.AwsEmailDeliveryParam{
+			Conf: &cfg.AmazonSesSettings{
+				Enable:    c.AmazonSesSettings.Enable,
+				Sort:      c.AmazonSesSettings.Sort,
+				PoolSize:  c.AmazonSesSettings.PoolSize,
+				Region:    c.AmazonSesSettings.Region,
+				Sender:    c.AmazonSesSettings.Sender,
+				Charset:   c.AmazonSesSettings.Charset,
+				AccessKey: c.AmazonSesSettings.AccessKey,
+				SecretKey: c.AmazonSesSettings.SecretKey,
+				Token:     c.AmazonSesSettings.Token,
+			},
+		})
+
+		param.EmailProviders = append(param.EmailProviders, useD.EmailProvider{
+			Sort: int64(c.AmazonSesSettings.Sort),
+			Repo: sesRepo,
+		})
+	}
+
+	if c.SMTPConfig.Enable {
+		smtpRepo := repository.MustSMTPUseCase(repository.SMTPMailUseCaseParam{
+			Conf: cfg.SMTPConfig{
+				Enable:           c.SMTPConfig.Enable,
+				Sort:             c.SMTPConfig.Sort,
+				GoroutinePoolNum: c.SMTPConfig.GoroutinePoolNum,
+				Host:             c.SMTPConfig.Host,
+				Port:             c.SMTPConfig.Port,
+				Username:         c.SMTPConfig.Username,
+				Password:         c.SMTPConfig.Password,
+			},
+		})
+
+		param.EmailProviders = append(param.EmailProviders, useD.EmailProvider{
+			Sort: int64(c.SMTPConfig.Sort),
+			Repo: smtpRepo,
+		})
+	}
+
+	if c.MitakeSMSSender.Enable {
+		param.SMSProviders = append(param.SMSProviders, useD.SMSProvider{
+			Sort: int64(c.MitakeSMSSender.Sort),
+			Repo: repository.MustMitakeRepository(repository.MitakeSMSDeliveryParam{
+				Conf: &cfg.MitakeSMSSender{
+					Enable:   c.MitakeSMSSender.Enable,
+					Sort:     c.MitakeSMSSender.Sort,
+					User:     c.MitakeSMSSender.User,
+					Password: c.MitakeSMSSender.Password,
+					PoolSize: c.MitakeSMSSender.PoolSize,
+				},
+			}),
+		})
+	}
+
+	uc := usecase.MustDeliveryUseCase(param)
+
 	return &ServiceContext{
-		Config:     c,
-		MailSender: usecase.MustMailgunUseCase(usecase.MailUseCaseParam{Conf: c}),
-		SMSSender:  usecase.MustMitakeUseCase(usecase.SMSUseCaseParam{Conf: c}),
-		Validate:   v.MustValidator(),
+		Config:          c,
+		DeliveryUseCase: uc,
+		TemplateUseCase: usecase.MustTemplateUseCase(usecase.TemplateUseCaseParam{}),
 	}
 }
diff --git a/internal/usecase/mitake.go b/internal/usecase/mitake.go
deleted file mode 100644
index ac332bc..0000000
--- a/internal/usecase/mitake.go
+++ /dev/null
@@ -1,36 +0,0 @@
-package usecase
-
-import (
-	"app-cloudep-notification-service/internal/config"
-	"app-cloudep-notification-service/internal/domain/usecase"
-	"context"
-
-	"github.com/minchao/go-mitake"
-)
-
-type SMSUseCaseParam struct {
-	Conf config.Config
-}
-
-type SMSUseCase struct {
-	Client *mitake.Client
-}
-
-func (s *SMSUseCase) SendSMS(_ context.Context, req usecase.SMSReq) error {
-	message := mitake.Message{
-		Dstaddr:  req.RecipientAddress,
-		Destname: req.RecipientName,
-		Smbody:   req.Body,
-	}
-	_, err := s.Client.Send(message)
-	if err != nil {
-		return err
-	}
-	return nil
-}
-
-func MustMitakeUseCase(param SMSUseCaseParam) usecase.SMSClientUseCase {
-	return &SMSUseCase{
-		Client: mitake.NewClient(param.Conf.SMSSender.User, param.Conf.SMSSender.Password, nil),
-	}
-}
diff --git a/internal/usecase/smtp.go b/internal/usecase/smtp.go
deleted file mode 100644
index f4d97e7..0000000
--- a/internal/usecase/smtp.go
+++ /dev/null
@@ -1,56 +0,0 @@
-package usecase
-
-import (
-	"app-cloudep-notification-service/internal/config"
-	"app-cloudep-notification-service/internal/domain/usecase"
-
-	pool "code.30cm.net/digimon/library-go/worker_pool"
-	"github.com/zeromicro/go-zero/core/logx"
-
-	"context"
-
-	"gopkg.in/gomail.v2"
-)
-
-type MailUseCaseParam struct {
-	Conf config.Config
-}
-
-type MailUseCase struct {
-	Host     string
-	Port     int
-	User     string
-	Password string
-	Pool     pool.WorkerPool
-}
-
-func (mu *MailUseCase) SendMail(_ context.Context, req usecase.MailReq) error {
-	// 用 goroutine pool 送,否則會超時
-	err := mu.Pool.Submit(func() {
-		m := gomail.NewMessage()
-		m.SetHeader("From", req.From)
-		m.SetHeader("To", req.To)
-		m.SetHeader("Subject", req.Subject)
-		m.SetBody("text/html", req.Body)
-		d := gomail.NewDialer(mu.Host, mu.Port, mu.User, mu.Password)
-		if err := d.DialAndSend(m); err != nil {
-			logx.WithCallerSkip(1).WithFields(
-				logx.Field("func", "MailUseCase.SendMail"),
-				logx.Field("req", req),
-				logx.Field("err", err),
-			).Error("failed to send mail by mailgun")
-		}
-	})
-
-	return err
-}
-
-func MustMailgunUseCase(param MailUseCaseParam) usecase.MailClientUseCase {
-	return &MailUseCase{
-		Host:     param.Conf.SMTP.Host,
-		Port:     param.Conf.SMTP.Port,
-		User:     param.Conf.SMTP.User,
-		Password: param.Conf.SMTP.Password,
-		Pool:     pool.NewWorkerPool(2000),
-	}
-}
diff --git a/notification.go b/notification.go
index b35a92c..95fe1ec 100644
--- a/notification.go
+++ b/notification.go
@@ -2,7 +2,8 @@ package main
 
 import (
 	"flag"
-	"fmt"
+
+	"github.com/zeromicro/go-zero/core/logx"
 
 	"app-cloudep-notification-service/gen_result/pb/notification"
 	"app-cloudep-notification-service/internal/config"
@@ -34,6 +35,6 @@ func main() {
 	})
 	defer s.Stop()
 
-	fmt.Printf("Starting rpc server at %s...\n", c.ListenOn)
+	logx.Infof("Starting rpc server at %s...\n", c.ListenOn)
 	s.Start()
 }
diff --git a/pkg/config/config.go b/pkg/config/config.go
new file mode 100644
index 0000000..054b66a
--- /dev/null
+++ b/pkg/config/config.go
@@ -0,0 +1,34 @@
+package config
+
+type SMTPConfig struct {
+	Enable           bool
+	Sort             int
+	GoroutinePoolNum int
+
+	Host     string
+	Port     int
+	Username string
+	Password string
+}
+
+type AmazonSesSettings struct {
+	Enable   bool
+	Sort     int
+	PoolSize int
+
+	Region    string
+	Sender    string
+	Charset   string
+	AccessKey string
+	SecretKey string
+	Token     string
+}
+
+type MitakeSMSSender struct {
+	Enable   bool
+	Sort     int
+	PoolSize int
+
+	User     string
+	Password string
+}
diff --git a/pkg/domain/error.go b/pkg/domain/error.go
new file mode 100644
index 0000000..0db475c
--- /dev/null
+++ b/pkg/domain/error.go
@@ -0,0 +1,28 @@
+package domain
+
+import (
+	"fmt"
+	"strings"
+
+	ers "code.30cm.net/digimon/library-go/errs"
+	"code.30cm.net/digimon/library-go/errs/code"
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+func ThirdPartyError(scope uint32, ec ers.ErrorCode, s ...string) *ers.LibError {
+	return ers.NewError(scope, code.ThirdParty, ec.ToUint32(), fmt.Sprintf("thirty error: %s", strings.Join(s, " ")))
+}
+
+func ThirdPartyErrorL(scope uint32, ec ers.ErrorCode,
+	l logx.Logger, filed []logx.LogField, s ...string) *ers.LibError {
+	e := ThirdPartyError(scope, ec, s...)
+	l.WithCallerSkip(1).WithFields(filed...).Error(e.Error())
+
+	return e
+}
+
+const (
+	NotificationErrorCode = 1 + iota
+	FailedToSendEmailErrorCode
+	FailedToSendSMSErrorCode
+)
diff --git a/pkg/domain/notification/language.go b/pkg/domain/notification/language.go
new file mode 100644
index 0000000..693fb68
--- /dev/null
+++ b/pkg/domain/notification/language.go
@@ -0,0 +1,9 @@
+package notification
+
+// Language 定義模板請求
+type Language string
+
+const (
+	LanguageZhTW Language = "zh-tw"
+	LanguageEnUS Language = "en-us"
+)
diff --git a/pkg/domain/notification/type.go b/pkg/domain/notification/type.go
new file mode 100644
index 0000000..fc0810a
--- /dev/null
+++ b/pkg/domain/notification/type.go
@@ -0,0 +1,16 @@
+package notification
+
+import "fmt"
+
+type TypeID int64
+
+func (id TypeID) String() string {
+	return fmt.Sprintf("%4d", id)
+}
+
+// 驗證碼通知類 0 ~ 100
+const (
+	BindingEmail         TypeID = 1 // 驗證碼:綁定 Email
+	BindingPhone         TypeID = 2 // 驗證碼:綁定 手機
+	ForgetPasswordVerify TypeID = 3 // 驗證碼: 忘記密碼
+)
diff --git a/pkg/domain/repository/mail.go b/pkg/domain/repository/mail.go
new file mode 100644
index 0000000..f95f764
--- /dev/null
+++ b/pkg/domain/repository/mail.go
@@ -0,0 +1,15 @@
+package repository
+
+import "context"
+
+type MailRepository interface {
+	// SendMail 送出 Email
+	SendMail(ctx context.Context, req MailReq) error
+}
+
+type MailReq struct {
+	To      []string
+	From    string
+	Subject string
+	Body    string
+}
diff --git a/pkg/domain/repository/sms.go b/pkg/domain/repository/sms.go
new file mode 100644
index 0000000..b9b230e
--- /dev/null
+++ b/pkg/domain/repository/sms.go
@@ -0,0 +1,16 @@
+package repository
+
+import (
+	"context"
+)
+
+type SMSClientRepository interface {
+	SendSMS(ctx context.Context, req SMSMessageRequest) error
+}
+
+// SMSMessageRequest SMS 訊息請求結構
+type SMSMessageRequest struct {
+	PhoneNumber    string `json:"phone_number" validate:"required,e164"` // 接收者號碼 (e164 格式用於驗證國際號碼)
+	RecipientName  string `json:"recipient_name" validate:"required"`    // 接收者姓名
+	MessageContent string `json:"message_content" validate:"required"`   // 要傳送的訊息
+}
diff --git a/pkg/domain/template/const.go b/pkg/domain/template/const.go
new file mode 100644
index 0000000..3e2d6a3
--- /dev/null
+++ b/pkg/domain/template/const.go
@@ -0,0 +1,12 @@
+package template
+
+// ==============================
+// 產品資訊常數
+// ==============================
+
+const (
+	ProductName      = "TrueHeart 團隊"
+	ProductLink      = "https://code.30cm.net"
+	ProductLogo      = "https://true-heart-dev.s3.ap-northeast-3.amazonaws.com/f70904eb-1a29-40f7-8940-9a124f23793a.png"
+	ProductCopyright = "© 2025 TrueHeart Inc. 版權所有"
+)
diff --git a/pkg/domain/template/email.go b/pkg/domain/template/email.go
new file mode 100644
index 0000000..652ff36
--- /dev/null
+++ b/pkg/domain/template/email.go
@@ -0,0 +1,128 @@
+package template
+
+import (
+	"github.com/matcornic/hermes/v2"
+)
+
+// ==============================
+// Email 結構定義
+// ==============================
+
+// EmailTemplate 代表 Email 樣板
+type EmailTemplate struct {
+	Title string
+	Body  string
+}
+
+// ProductInfo 包含產品相關的資訊
+type ProductInfo struct {
+	Name      string
+	Link      string
+	Logo      string
+	Copyright string
+}
+
+// EmailBodyContent 包含郵件正文的資訊
+type EmailBodyContent struct {
+	RecipientName string
+	Intros        []string
+	Actions       []hermes.Action
+	Outros        []string
+	Signature     string
+}
+
+// EmailContentParams 組成 Email 內容的參數
+type EmailContentParams struct {
+	Product ProductInfo
+	Content EmailBodyContent
+}
+
+// ==============================
+// Email 內容產生器
+// ==============================
+
+// GenerateEmailBody 根據參數產生 Email HTML 內容
+func GenerateEmailBody(params EmailContentParams) (string, error) {
+	product := hermes.Product{
+		Name:      params.Product.Name,
+		Link:      params.Product.Link,
+		Logo:      params.Product.Logo,
+		Copyright: params.Product.Copyright,
+	}
+
+	body := hermes.Body{
+		Name:      params.Content.RecipientName,
+		Intros:    params.Content.Intros,
+		Actions:   params.Content.Actions,
+		Outros:    params.Content.Outros,
+		Signature: params.Content.Signature,
+	}
+
+	h := hermes.Hermes{Product: product}
+	email := hermes.Email{Body: body}
+
+	return h.GenerateHTML(email)
+}
+
+// GenerateEmailContent 生成 Email 內容(可用於不同驗證類型)
+func GenerateEmailContent(title string, intros []string) (EmailTemplate, error) {
+	req := EmailContentParams{
+		Product: ProductInfo{
+			Name:      ProductName,
+			Link:      ProductLink,
+			Logo:      ProductLogo,
+			Copyright: ProductCopyright,
+		},
+		Content: EmailBodyContent{
+			RecipientName: "{{.Username}}",
+			Intros:        intros,
+			Actions: []hermes.Action{
+				{
+					Instructions: "請複製您的驗證碼,到網頁重置",
+					InviteCode:   "{{.VerifyCode}}",
+				},
+			},
+			Outros: []string{
+				"如果您沒有請求此操作,請忽略此郵件。",
+			},
+			Signature: "",
+		},
+	}
+
+	emailBody, err := GenerateEmailBody(req)
+	if err != nil {
+		return EmailTemplate{}, err
+	}
+
+	return EmailTemplate{
+		Title: title,
+		Body:  emailBody,
+	}, nil
+}
+
+// ==============================
+// Email 產生函數
+// ==============================
+
+// GenerateForgetPasswordEmailZHTW 生成繁體中文的忘記密碼驗證信
+func GenerateForgetPasswordEmailZHTW() (EmailTemplate, error) {
+	return GenerateEmailContent("TrueHeart 重設密碼驗證信",
+		[]string{"您收到此電子郵件是因為我們收到了針對帳戶的密碼重置請求。"})
+}
+
+// GenerateBindingEmailZHTW 生成綁定帳號驗證信
+func GenerateBindingEmailZHTW() (EmailTemplate, error) {
+	return GenerateEmailContent("TrueHeart 綁定信箱驗證信",
+		[]string{"您收到此電子郵件是因為我們收到了針對帳戶的 Email 認證請求。"})
+}
+
+// ==============================
+// Email 模板對應表
+// ==============================
+
+var EmailTemplateMap = map[Language]map[Type]func() (EmailTemplate, error){
+	LanguageZhTW: {
+		ForgetPasswordVerify: GenerateForgetPasswordEmailZHTW,
+		BindingEmail:         GenerateBindingEmailZHTW,
+	},
+}
diff --git a/pkg/domain/template/language.go b/pkg/domain/template/language.go
new file mode 100644
index 0000000..2be8fcb
--- /dev/null
+++ b/pkg/domain/template/language.go
@@ -0,0 +1,11 @@
+package template
+
+// ==============================
+// 語言與驗證碼類型
+// ==============================
+
+type Language string
+
+const (
+	LanguageZhTW Language = "zh-tw"
+)
diff --git a/pkg/domain/template/type.go b/pkg/domain/template/type.go
new file mode 100644
index 0000000..ea74771
--- /dev/null
+++ b/pkg/domain/template/type.go
@@ -0,0 +1,9 @@
+package template
+
+type Type string
+
+const (
+	BindingEmail         Type = "binding_email"   // 驗證碼:綁定 Email
+	BindingPhone         Type = "binding_phone"   // 驗證碼:綁定 手機
+	ForgetPasswordVerify Type = "forget_password" // 驗證碼: 忘記密碼
+)
diff --git a/pkg/domain/usecase/delivary.go b/pkg/domain/usecase/delivary.go
new file mode 100644
index 0000000..5bd3957
--- /dev/null
+++ b/pkg/domain/usecase/delivary.go
@@ -0,0 +1,43 @@
+package usecase
+
+import (
+	"app-cloudep-notification-service/pkg/domain/repository"
+	"context"
+)
+
+type DeliveryUseCase interface {
+	SendMessage(ctx context.Context, req SMSMessageRequest) error
+	SendEmail(ctx context.Context, req MailReq) error
+}
+
+type MailReq struct {
+	To      []string
+	From    string
+	Subject string
+	Body    string
+}
+
+type SMSMessageRequest struct {
+	PhoneNumber    string `json:"phone_number" validate:"required,e164"` // 接收者號碼 (e164 格式用於驗證國際號碼)
+	RecipientName  string `json:"recipient_name" validate:"required"`    // 接收者姓名
+	MessageContent string `json:"message_content" validate:"required"`   // 要傳送的訊息
+}
+
+type EmailTemplateResp struct {
+	Subject string `json:"subject"` // 郵件主題
+	Body    string `json:"body"`    // 郵件內容
+}
+
+type SMSTemplateResp struct {
+	Body string `json:"body"`
+}
+
+type SMSProvider struct {
+	Sort int64
+	Repo repository.SMSClientRepository
+}
+
+type EmailProvider struct {
+	Sort int64
+	Repo repository.MailRepository
+}
diff --git a/pkg/domain/usecase/template.go b/pkg/domain/usecase/template.go
new file mode 100644
index 0000000..20e8c55
--- /dev/null
+++ b/pkg/domain/usecase/template.go
@@ -0,0 +1,10 @@
+package usecase
+
+import (
+	"app-cloudep-notification-service/pkg/domain/template"
+	"context"
+)
+
+type TemplateUseCase interface {
+	GetEmailTemplateByStatic(ctx context.Context, language template.Language, templateID template.Type) (template.EmailTemplate, error)
+}
diff --git a/pkg/repository/aws_ses_mailer.go b/pkg/repository/aws_ses_mailer.go
new file mode 100644
index 0000000..229cb4a
--- /dev/null
+++ b/pkg/repository/aws_ses_mailer.go
@@ -0,0 +1,109 @@
+package repository
+
+import (
+	"app-cloudep-notification-service/pkg/config"
+	"app-cloudep-notification-service/pkg/domain"
+	"app-cloudep-notification-service/pkg/domain/repository"
+	"context"
+	"time"
+
+	"code.30cm.net/digimon/library-go/errs/code"
+	pool "code.30cm.net/digimon/library-go/worker_pool"
+	"github.com/aws/aws-sdk-go-v2/credentials"
+	"github.com/aws/aws-sdk-go-v2/service/ses/types"
+	"github.com/zeromicro/go-zero/core/logx"
+
+	"github.com/aws/aws-sdk-go-v2/aws"
+	"github.com/aws/aws-sdk-go-v2/service/ses"
+)
+
+// AwsEmailDeliveryParam 傳送參數配置
+type AwsEmailDeliveryParam struct {
+	Conf *config.AmazonSesSettings
+}
+
+type AwsEmailDeliveryRepository struct {
+	Client *ses.Client
+	Pool   pool.WorkerPool
+}
+
+func MustAwsSesMailRepository(param AwsEmailDeliveryParam) repository.MailRepository {
+	// 手動指定 AWS 配置,不使用默認配置
+	cfg := aws.Config{
+		Region: param.Conf.Region, // 自定義的 AWS 區域
+		Credentials: credentials.NewStaticCredentialsProvider(
+			param.Conf.AccessKey, // AWS Access Key
+			param.Conf.SecretKey, // AWS Secret Key
+			"",
+		),
+	}
+	// 創建 SES 客戶端
+	sesClient := ses.NewFromConfig(cfg)
+
+	return &AwsEmailDeliveryRepository{
+		Client: sesClient,
+		Pool:   pool.NewWorkerPool(param.Conf.PoolSize),
+	}
+}
+
+func (use *AwsEmailDeliveryRepository) SendMail(ctx context.Context, req repository.MailReq) error {
+	err := use.Pool.Submit(func() {
+		// 設置郵件參數
+		to := make([]string, 0, len(req.To))
+		to = append(to, req.To...)
+
+		input := &ses.SendEmailInput{
+			Destination: &types.Destination{
+				ToAddresses: to,
+			},
+			Message: &types.Message{
+				Body: &types.Body{
+					Html: &types.Content{
+						Charset: aws.String("UTF-8"),
+						Data:    aws.String(req.Body),
+					},
+				},
+				Subject: &types.Content{
+					Charset: aws.String("UTF-8"),
+					Data:    aws.String(req.Subject),
+				},
+			},
+			Source: aws.String(req.From),
+		}
+
+		// 發送郵件
+		// TODO 不明原因送不出去,會被 context cancel 這裡先把它手動加到100sec
+		newCtx, cancel := context.WithTimeout(context.Background(), 100*time.Second)
+		defer cancel()
+
+		//nolint:contextcheck
+		if _, err := use.Client.SendEmail(newCtx, input); err != nil {
+			_ = domain.ThirdPartyErrorL(
+				code.CloudEPNotification,
+				domain.FailedToSendEmailErrorCode,
+				logx.WithContext(ctx),
+				[]logx.LogField{
+					{Key: "req", Value: req},
+					{Key: "func", Value: "AwsEmailDeliveryU.SendEmail"},
+					{Key: "err", Value: err.Error()},
+				},
+				"failed to send mail by aws ses")
+		}
+	})
+	if err != nil {
+		e := domain.ThirdPartyErrorL(
+			code.CloudEPNotification,
+			domain.FailedToSendEmailErrorCode,
+			logx.WithContext(ctx),
+			[]logx.LogField{
+				{Key: "req", Value: req},
+				{Key: "func", Value: "AwsEmailDeliveryU.SendEmail"},
+				{Key: "err", Value: err.Error()},
+			},
+			"failed to send mail by aws ses")
+
+		return e
+	}
+
+	return nil
+}
diff --git a/pkg/repository/mitake_sms_sender.go b/pkg/repository/mitake_sms_sender.go
new file mode 100644
index 0000000..fc2ec23
--- /dev/null
+++ b/pkg/repository/mitake_sms_sender.go
@@ -0,0 +1,63 @@
+package repository
+
+import (
+	"app-cloudep-notification-service/pkg/config"
+	"app-cloudep-notification-service/pkg/domain"
+	"app-cloudep-notification-service/pkg/domain/repository"
+	"context"
+
+	"code.30cm.net/digimon/library-go/errs/code"
+	pool "code.30cm.net/digimon/library-go/worker_pool"
+	"github.com/minchao/go-mitake"
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+// MitakeSMSDeliveryParam 三竹傳送參數配置
+type MitakeSMSDeliveryParam struct {
+	Conf *config.MitakeSMSSender
+}
+
+type MitakeSMSDeliveryRepository struct {
+	Client *mitake.Client
+	Pool   pool.WorkerPool
+}
+
+func (use *MitakeSMSDeliveryRepository) SendSMS(ctx context.Context, req repository.SMSMessageRequest) error {
+	// 用 goroutine pool 送,否則會超時
+	err := use.Pool.Submit(func() {
+		message := mitake.Message{
+			Dstaddr:  req.PhoneNumber,
+			Destname: req.RecipientName,
+			Smbody:   req.MessageContent,
+		}
+		_, err := use.Client.Send(message)
+		if err != nil {
+			logx.Error("failed to send sms via mitake")
+		}
+	})
+
+	if err != nil {
+		// 錯誤代碼 20-201-04
+		e := domain.ThirdPartyErrorL(
+			code.CloudEPNotification,
+			domain.FailedToSendSMSErrorCode,
+			logx.WithContext(ctx),
+			[]logx.LogField{
+				{Key: "req", Value: req},
+				{Key: "func", Value: "MitakeSMSDeliveryRepository.Client.Send"},
+				{Key: "err", Value: err.Error()},
+			},
+			"failed to send sns by mitake").Wrap(err)
+
+		return e
+	}
+
+	return nil
+}
+
+func MustMitakeRepository(param MitakeSMSDeliveryParam) repository.SMSClientRepository {
+	return &MitakeSMSDeliveryRepository{
+		Client: mitake.NewClient(param.Conf.User, param.Conf.Password, nil),
+		Pool:   pool.NewWorkerPool(param.Conf.PoolSize),
+	}
+}
diff --git a/pkg/repository/smtp_mailer.go b/pkg/repository/smtp_mailer.go
new file mode 100644
index 0000000..0c13a24
--- /dev/null
+++ b/pkg/repository/smtp_mailer.go
@@ -0,0 +1,52 @@
+package repository
+
+import (
+	"app-cloudep-notification-service/pkg/config"
+	"app-cloudep-notification-service/pkg/domain/repository"
+	"context"
+
+	pool "code.30cm.net/digimon/library-go/worker_pool"
+	"github.com/zeromicro/go-zero/core/logx"
+	"gopkg.in/gomail.v2"
+)
+
+type SMTPMailUseCaseParam struct {
+	Conf config.SMTPConfig
+}
+
+type SMTPMailRepository struct {
+	Client *gomail.Dialer
+	Pool   pool.WorkerPool
+}
+
+func MustSMTPUseCase(param SMTPMailUseCaseParam) repository.MailRepository {
+	return &SMTPMailRepository{
+		Client: gomail.NewDialer(
+			param.Conf.Host,
+			param.Conf.Port,
+			param.Conf.Username,
+			param.Conf.Password,
+		),
+		Pool: pool.NewWorkerPool(param.Conf.GoroutinePoolNum),
+	}
+}
+
+func (repo *SMTPMailRepository) SendMail(_ context.Context, req repository.MailReq) error {
+	// 用 goroutine pool 送,否則會超時
+	err := repo.Pool.Submit(func() {
+		m := gomail.NewMessage()
+		m.SetHeader("From", req.From)
+		m.SetHeader("To", req.To...)
+		m.SetHeader("Subject", req.Subject)
+		m.SetBody("text/html", req.Body)
+		if err := repo.Client.DialAndSend(m); err != nil {
+			logx.WithCallerSkip(1).WithFields(
+				logx.Field("func", "MailUseCase.SendMail"),
+				logx.Field("req", req),
+				logx.Field("err", err),
+			).Error("failed to send mail by mailgun")
+		}
+	})
+
+	return err
+}
diff --git a/pkg/usecase/delivery.go b/pkg/usecase/delivery.go
new file mode 100644
index 0000000..1c05c72
--- /dev/null
+++ b/pkg/usecase/delivery.go
@@ -0,0 +1,75 @@
+package usecase
+
+import (
+	"app-cloudep-notification-service/pkg/domain/repository"
+	"app-cloudep-notification-service/pkg/domain/usecase"
+	"context"
+	"sort"
+	"time"
+)
+
+// DeliveryUseCaseParam 傳送參數配置
+type DeliveryUseCaseParam struct {
+	SMSProviders   []usecase.SMSProvider
+	EmailProviders []usecase.EmailProvider
+}
+
+// DeliveryUseCase 通知
+type DeliveryUseCase struct {
+	param DeliveryUseCaseParam
+}
+
+func MustDeliveryUseCase(param DeliveryUseCaseParam) usecase.DeliveryUseCase {
+	return &DeliveryUseCase{
+		param: param,
+	}
+}
+
+func (use *DeliveryUseCase) SendMessage(ctx context.Context, req usecase.SMSMessageRequest) error {
+	var err error
+	// 根據 Sort 欄位對 SMSProviders 進行排序
+	sort.Slice(use.param.SMSProviders, func(i, j int) bool {
+		return use.param.SMSProviders[i].Sort < use.param.SMSProviders[j].Sort
+	})
+
+	newCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
+	defer cancel()
+
+	// 依序嘗試發送
+	for _, provider := range use.param.SMSProviders {
+		if err = provider.Repo.SendSMS(newCtx, repository.SMSMessageRequest{
+			PhoneNumber:    req.PhoneNumber,
+			RecipientName:  req.RecipientName,
+			MessageContent: req.MessageContent,
+		}); err == nil {
+			return nil // 發送成功
+		}
+	}
+
+	return err
+}
+
+func (use *DeliveryUseCase) SendEmail(ctx context.Context, req usecase.MailReq) error {
+	var err error
+	// 根據 Sort 欄位對 SMSProviders 進行排序
+	sort.Slice(use.param.EmailProviders, func(i, j int) bool {
+		return use.param.EmailProviders[i].Sort < use.param.EmailProviders[j].Sort
+	})
+
+	newCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
+	defer cancel()
+
+	// 依序嘗試發送 dreq
+	for _, provider := range use.param.EmailProviders {
+		if err = provider.Repo.SendMail(newCtx, repository.MailReq{
+			From:    req.From,
+			To:      req.To,
+			Subject: req.Subject,
+			Body:    req.Body,
+		}); err == nil {
+			return nil // 發送成功
+		}
+	}
+
+	return err
+}
diff --git a/pkg/usecase/template.go b/pkg/usecase/template.go
new file mode 100644
index 0000000..965221c
--- /dev/null
+++ b/pkg/usecase/template.go
@@ -0,0 +1,43 @@
+package usecase
+
+import (
+	"app-cloudep-notification-service/pkg/domain/template"
+	"app-cloudep-notification-service/pkg/domain/usecase"
+	"context"
+	"fmt"
+)
+
+type TemplateUseCaseParam struct{}
+
+type TemplateUseCase struct {
+	TemplateUseCaseParam
+}
+
+func MustTemplateUseCase(param TemplateUseCaseParam) usecase.TemplateUseCase {
+	return &TemplateUseCase{
+		param,
+	}
+}
+
+func (use *TemplateUseCase) GetEmailTemplateByStatic(_ context.Context, language template.Language, templateID template.Type) (template.EmailTemplate, error) {
+	// 查找指定語言的模板映射
+	templateByLang, exists := template.EmailTemplateMap[language]
+	if !exists {
+		return template.EmailTemplate{}, fmt.Errorf("email template not found for language: %s", language)
+	}
+
+	// 查找指定類型的模板生成函數
+	templateFunc, exists := templateByLang[templateID]
+	if !exists {
+		return template.EmailTemplate{}, fmt.Errorf("email template not found for type ID: %s", templateID)
+	}
+
+	// 執行模板生成函數
+	tmp, err := templateFunc()
+	if err != nil {
+		return template.EmailTemplate{}, fmt.Errorf("error generating email template: %w", err)
+	}
+
+	// 返回構建好的響應
+	return tmp, nil
+}