Initial commit: Happy Up monorepo through Sprint 5.
Document-driven MVP with FastAPI backend, Vue H5, WeChat mini shell, product demo, and Docker dev stack. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
from datetime import datetime, timezone
|
||||
from typing import Any
|
||||
from uuid import uuid4
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class ApiMeta(BaseModel):
|
||||
request_id: str = Field(alias="requestId")
|
||||
timestamp: str
|
||||
trace_id: str | None = Field(default=None, alias="traceId")
|
||||
|
||||
model_config = {"populate_by_name": True}
|
||||
|
||||
|
||||
class StandardResponse(BaseModel):
|
||||
code: int = 0
|
||||
message: str = "ok"
|
||||
data: Any | None = None
|
||||
meta: ApiMeta
|
||||
|
||||
model_config = {"populate_by_name": True}
|
||||
|
||||
|
||||
def build_meta(request_id: str | None = None) -> ApiMeta:
|
||||
return ApiMeta(
|
||||
requestId=request_id or uuid4().hex,
|
||||
timestamp=datetime.now(timezone.utc).isoformat(),
|
||||
traceId=uuid4().hex[:16],
|
||||
)
|
||||
|
||||
|
||||
def ok(data: Any = None, message: str = "ok", request_id: str | None = None) -> dict[str, Any]:
|
||||
return StandardResponse(code=0, message=message, data=data, meta=build_meta(request_id)).model_dump(
|
||||
by_alias=True
|
||||
)
|
||||
|
||||
|
||||
def error(code: int, message: str, request_id: str | None = None, **details: Any) -> dict[str, Any]:
|
||||
payload = {"errorCode": message, **details} if details else {"errorCode": message}
|
||||
return StandardResponse(code=code, message=message, data=payload, meta=build_meta(request_id)).model_dump(
|
||||
by_alias=True
|
||||
)
|
||||
@@ -0,0 +1,28 @@
|
||||
import hashlib
|
||||
from datetime import datetime, timedelta, timezone
|
||||
|
||||
import jwt
|
||||
|
||||
from app.config import settings
|
||||
|
||||
|
||||
def hash_phone(phone: str) -> str:
|
||||
normalized = "".join(ch for ch in phone if ch.isdigit())
|
||||
return hashlib.sha256(normalized.encode("utf-8")).hexdigest()
|
||||
|
||||
|
||||
def mask_phone(phone: str) -> str:
|
||||
digits = "".join(ch for ch in phone if ch.isdigit())
|
||||
if len(digits) < 7:
|
||||
return phone
|
||||
return f"{digits[:3]}****{digits[-4:]}"
|
||||
|
||||
|
||||
def create_access_token(user_id: int, role: str) -> str:
|
||||
expire = datetime.now(timezone.utc) + timedelta(minutes=settings.jwt_expire_minutes)
|
||||
payload = {"sub": str(user_id), "role": role, "exp": expire}
|
||||
return jwt.encode(payload, settings.jwt_secret, algorithm="HS256")
|
||||
|
||||
|
||||
def decode_access_token(token: str) -> dict:
|
||||
return jwt.decode(token, settings.jwt_secret, algorithms=["HS256"])
|
||||
Reference in New Issue
Block a user