完善后的服务端与客户端实现代码,重点优化了代码传递与确认流程:
---
### 服务端完整代码 (server.py)
```python
from fastapi import FastAPI, Depends, HTTPException, status
from pydantic import BaseModel
from datetime import datetime, timedelta
import sqlite3
import uuid
import hashlib
from jose import JWTError, jwt
from passlib.context import CryptContext
import secrets
# 配置参数
SECRET_KEY = secrets.token_hex(32)
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
app = FastAPI()
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
# 数据库连接
conn = sqlite3.connect('service.db', check_same_thread=False)
c = conn.cursor()
# 初始化数据库
c.execute('''CREATE TABLE IF NOT EXISTS users
(id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE,
password_hash TEXT,
balance INTEGER DEFAULT 0)''')
c.execute('''CREATE TABLE IF NOT EXISTS services
(id INTEGER PRIMARY KEY,
name TEXT,
price INTEGER)''')
c.execute('''CREATE TABLE IF NOT EXISTS user_services
(user_id INTEGER,
service_id INTEGER,
PRIMARY KEY(user_id, service_id))''')
c.execute('''CREATE TABLE IF NOT EXISTS activation_codes
(code TEXT PRIMARY KEY,
user_id INTEGER,
service_id INTEGER,
expires_at DATETIME,
used BOOLEAN DEFAULT FALSE)''')
# 初始化服务数据
services_data = [(i, f"服务{i}", i*10) for i in range(1,6)]
c.executemany("INSERT OR IGNORE INTO services VALUES (?,?,?)", services_data)
conn.commit()
class UserCreate(BaseModel):
username: str
password: str
class UserLogin(BaseModel):
username: str
password: str
class ServiceSelection(BaseModel):
selected: list[int]
class Token(BaseModel):
access_token: str
token_type: str
# 工具函数
def verify_password(plain_password, hashed_password):
return pwd_context.verify(plain_password, hashed_password)
def get_password_hash(password):
return pwd_context.hash(password)
def create_access_token(data: dict):
to_encode = data.copy()
expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
to_encode.update({"exp": expire})
return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
async def get_current_user(token: str = Depends(oauth2_scheme)):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="无效的认证凭证",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise credentials_exception
except JWTError:
raise credentials_exception
c.execute("SELECT id, username FROM users WHERE username=?", (username,))
user = c.fetchone()
if user is None:
raise credentials_exception
return user
# 路由
@app.post("/register", status_code=201)
def register(user: UserCreate):
hashed_pw = get_password_hash(user.password)
try:
c.execute("INSERT INTO users (username, password_hash) VALUES (?,?)",
(user.username, hashed_pw))
conn.commit()
return {"detail": "用户注册成功"}
except sqlite3.IntegrityError:
raise HTTPException(400, detail="用户名已存在")
@app.post("/login", response_model=Token)
def login(form_data: UserLogin):
c.execute("SELECT id, password_hash FROM users WHERE username=?", (form_data.username,))
user = c.fetchone()
if not user or not verify_password(form_data.password, user[1]):
raise HTTPException(400, detail="用户名或密码错误")
access_token = create_access_token(
data={"sub": form_data.username}
)
return {"access_token": access_token, "token_type": "bearer"}
@app.post("/save-selections")
def save_selections(selection: ServiceSelection, current_user: tuple = Depends(get_current_user)):
user_id = current_user[0]
# 删除旧选择
c.execute("DELETE FROM user_services WHERE user_id=?", (user_id,))
# 插入新选择
for service_id in selection.selected:
c.execute("INSERT INTO user_services VALUES (?,?)", (user_id, service_id))
conn.commit()
return {"status": "保存成功"}
@app.get("/my-services")
def get_my_services(current_