# 我直屬於你 - 文案
「 我直屬於你 」是海洋大學資工系直屬抽籤系統,特色為在抽籤後學長姐和學弟妹可以到系統上查詢自己的直屬及其個人資訊如姓名、聯絡方式等,並可以修改自己的個人資料;此外,由於學長姐是否接收直屬以及其願意接收直屬之人數採自願制,因此會產生學弟妹沒有直屬學長姐的情況,為此,我們基於微服務架構提供了使用專屬邀請碼功能,讓沒有直屬學長姐的學弟妹們可以透過專屬邀請碼和學長姐綁定直屬關係。
## 技術使用
### 後端 Backend
#### Go (Gin)
Go 語言以其高效能和低資源占用而著稱,適合處理需要高並發的應用。Gin 是 Go 中常用的網頁框架,提供了快速、輕量的 HTTP 服務,適合實現後端核心邏輯。
#### Node.js (Express.js)
Node.js 是一個事件驅動、非同步的 JavaScript 執行環境,適合構建高效、非阻塞的應用。Express.js 是一個簡單而靈活的 Node.js 框架,易於處理 API 請求和路由管理,為應用提供輕量而靈活的後端支援。
#### PostgreSQL
PostgreSQL 是一個關聯式資料庫系統,擁有強大的資料一致性、複雜查詢支持,以及事務處理能力,非常適合處理結構化資料和需要強一致性的情境。
#### MongoDB
MongoDB 是一個 NoSQL 資料庫,提供了靈活的文件型結構,特別適合存儲非結構化或半結構化資料,在動態變化的資料模型需求下具有極高的適應性。
#### Redis
Redis 作為快取層,是一個高效能的鍵值型 NoSQL 資料庫,能夠極大地提高資料的讀取速度,減少後端伺服器的負載。它的資料持久化選項和即時數據存取能力,非常適合提升系統效能和縮短回應時間。
### 前端 Frontend
#### React (Vite)
前端選用 React 作為框架,搭配 Vite 作為開發工具。React 提供了強大的組件化結構,便於進行重用和維護,Vite 具備極快的開發伺服器啟動速度和模組熱替換功能,顯著提升了開發效率。React 的虛擬 DOM 技術也有效優化了應用的渲染效能。
## 功能介紹
### 抽籤/抽籤動畫
使用 `useRef` 來保持 DOM 的穩定引用,解決動畫在第一次觸發時無法正常運作的問題。
```javascript=
const seniorRotateRef = useRef(null);
const animationControlSenior = async() => {
let btn = document.querySelector('#btn1');
if(btn.innerHTML === '開始') {
setTimeout(() => {
seniorRotateRef.current.style.transform = 'rotateX(180deg)';
}, 1000);
}
};
```
### 登錄系統
使用 `useNavigate` 夾帶登入資料跳轉到使用者畫面。
```javascript=
const navigate = useNavigate();
const handleLogin = async(event) => {
event.preventDefault();
try {
const response = fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application-json',
},
body: JSON.stringify(request);
});
if (response.ok) {
const result = await response.json();
navigate(`/${selectedIdentity}`, { state: result });
} else {
console.log(response.status);
}
} catch(error) {
console.log(error);
}
};
```
### 雙語環境
使用 `createContext` 管理語言環境,並將環境設定保存在 LocalStorage 以提高 UX。
```javascript=
export const language = createContext();
const LanProvider = ({ children }) => {
const initialLan = localStorage.getItem('lan') || 'zh';
const [lan, setLan] = useState(initialLan);
useEffect(() => {
localStorage.setItem('lan', lan);
}, [lan]);
return (
<language.Provider value={{ lan, setLan }}>
{children}
</language.Provider>
);
};
```
使用 `useContext` 取得語言全域變數
```javascript=
function LoginView() {
const { lan } = useContext(language);
return (
{
lan === 'zh' ? '顯示中文' : 'Display English'
}
);
};
```
### 查詢頁面
- 使用 `useEffect` 取得即時資料,並使用 Redis 來作為 Cache 提高效能
```go=
func (repo *UserRepository) GetUser(id string) (*model.User, error) {
val, err := repo.Cache.Get(db.Ctx, id).Result()
if err == redis.Nil {
var user model.User
if err := repo.Database.First(&user, "student_number = ?", id).Error; err != nil {
return nil, err
}
userJSON, err := json.Marshal(user)
if err != nil {
return nil, err
}
repo.Cache.Set(db.Ctx, id, userJSON, 0)
return &user, nil
} else if err != nil {
return nil, err
}
var user model.User
if err := json.Unmarshal([]byte(val), &user); err != nil {
return nil, err
}
return &user, nil
}
```
- 使用 `useLocation` 來取得 `useNavigate` 夾帶的資料
```javascript=
const location = useLocation();
const [ state, setState ] = useState(location.state);
```
### 邀請碼
- 基於微服務架構使用 Express + MongoDB 獨立設計邀請碼系統
- 學弟妹輸入想邀請的學長姐
- 生成專屬邀請碼,只允許雙方使用,並於 60 秒後過期
- 學長姐輸入該邀請碼即綁定成功後刪除該邀請碼
```javascript=
const invitationSchema = new mongoose.Schema({
code: { type: String, required: true },
expiresAt: { type: Date, default: Date.now, expires: 60 }
});
const Invitation = mongoose.model('invitation', invitationSchema);
function generateCode() {
return crypto.randomInt(10000, 99999).toString();
}
async function generate(junior, senior) {
let code;
while (true) {
code = generateCode();
const existing = await Invitation.findOne({ code });
if (!existing) break;
}
const invitation = new Invitation({
code,
});
await invitation.save();
return code;
}
```
### Excel 匯出抽籤結果
( 待完成 )
## 架構設計
### clean architecture
我採用了 Clean Architecture 來確保應用程式的可維護性,將應用的核心業務邏輯與框架、數據存取層和 UI 分離,便於日後的功能擴展、模組替換和單元測試。

### microservice architecture
使用微服務架構來將後端系統分解為多個獨立的服務模組,每個模組負責特定的業務功能,彼此之間通過 API 進行通信,提高系統的可擴展性、容錯性和部署靈活性,允許不同服務獨立開發、測試和部署。

## 開發流程
- pull request
- git model
- scrum (kanban, sprint)