Files
eryao/backend/src/models/user_feedback.py
T
qzl 6a2a9d2c87 feat(feedback): implement user feedback collection system with email reporting
Backend:
- Add user_feedback table with RLS policy
- Create feedback submission API (multipart/form-data)
- Implement xlsx report generation with embedded images
- Add scheduled email delivery via Feishu SMTP
- Create HTML email templates (daily_report, no_feedback)

Frontend:
- Add feedback screen with type selection and image picker
- Support anonymous submission via skipAuth flag
- Collect device info and app version

Protocol:
- Document feedback API contract and error codes
- Update http-error-codes.md with FEEDBACK_* codes
2026-04-20 12:49:54 +08:00

44 lines
1.5 KiB
Python

from __future__ import annotations
import uuid
from sqlalchemy import Index, String, Text, text
from sqlalchemy.dialects.postgresql import JSONB, UUID
from sqlalchemy.orm import Mapped, mapped_column
from core.db.base import Base, TimestampMixin
class UserFeedback(TimestampMixin, Base):
__tablename__ = "user_feedback"
__table_args__ = (
Index("ix_user_feedback_user_id", "user_id"),
Index("ix_user_feedback_created_at", "created_at"),
Index("ix_user_feedback_status", "status"),
)
id: Mapped[uuid.UUID] = mapped_column(
UUID(as_uuid=True),
server_default=text("gen_random_uuid()"),
primary_key=True,
)
user_id: Mapped[uuid.UUID | None] = mapped_column(
UUID(as_uuid=True),
nullable=True,
)
feedback_type: Mapped[str] = mapped_column(
String(20), nullable=False, server_default="other"
)
content: Mapped[str] = mapped_column(Text, nullable=False)
images: Mapped[list[str]] = mapped_column(
JSONB, nullable=False, server_default=text("'[]'::jsonb"), default=list
)
device_info: Mapped[dict] = mapped_column(
JSONB, nullable=False, server_default=text("'{}'::jsonb"), default=dict
)
app_version: Mapped[str] = mapped_column(String(20), nullable=False)
os_version: Mapped[str] = mapped_column(String(50), nullable=False)
status: Mapped[str] = mapped_column(
String(20), nullable=False, server_default="pending"
)