実装完了
Backend(Django) backend/apps/mail/serializers.py MailEmailListSerializer を新規追加(フロントエンド向けメール一覧用) feedback_token フィールドを含む(フィードバックリンク表示用) backend/apps/mail/views.py MailEmailCreateView → MailEmailView に変更(GET+POST を統合) GET /api/mail/emails/ : JWT認証でメール履歴取得(最新100件、account/verdict フィルター対応) POST /api/mail/emails/ : APIキー認証でWindmillからのメール記録(既存動作を維持) get_permissions() でメソッドごとに認証方法を切替 MailStatsView を新規追加 GET /api/mail/stats/ : 今日の処理件数、LINE通知数、フィードバック待ち、ルール数を返す backend/apps/mail/urls.py emails/ → MailEmailView(GET+POST) stats/ → MailStatsView を追加 Frontend(Next.js) frontend/src/app/mail/history/page.tsx (新規作成) メール処理履歴の一覧テーブル アカウント・LLM判定でフィルタリング可能 LLM判定・フィードバック状態をバッジで表示 フィードバックトークンがあれば「回答」リンクを表示 frontend/src/app/dashboard/page.tsx (再設計) 2カラムのモジュールカード形式に変更 作付け計画カード: 年度セレクタ、集計数値、作物別集計、クイックアクセス メール通知カード: 今日の処理件数、LINE通知数、フィードバック待ち、ルール数、メール履歴・ルール管理ボタン
This commit is contained in:
@@ -32,6 +32,24 @@ class MailEmailCreateSerializer(serializers.ModelSerializer):
|
||||
]
|
||||
|
||||
|
||||
class MailEmailListSerializer(serializers.ModelSerializer):
|
||||
"""フロントエンド向けメール一覧用"""
|
||||
feedback_token = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = MailEmail
|
||||
fields = [
|
||||
'id', 'account', 'sender_email', 'sender_domain',
|
||||
'subject', 'received_at', 'llm_verdict',
|
||||
'notified_at', 'feedback', 'feedback_at', 'feedback_token',
|
||||
]
|
||||
|
||||
def get_feedback_token(self, obj):
|
||||
if hasattr(obj, 'notification_token'):
|
||||
return str(obj.notification_token.token)
|
||||
return None
|
||||
|
||||
|
||||
class FeedbackDetailSerializer(serializers.ModelSerializer):
|
||||
"""フィードバックページ表示用"""
|
||||
class Meta:
|
||||
|
||||
@@ -9,7 +9,12 @@ urlpatterns = [
|
||||
# Windmill向けAPI(APIキー認証)
|
||||
path('sender-rule/', views.SenderRuleView.as_view(), name='mail-sender-rule'),
|
||||
path('sender-context/', views.SenderContextView.as_view(), name='mail-sender-context'),
|
||||
path('emails/', views.MailEmailCreateView.as_view(), name='mail-email-create'),
|
||||
|
||||
# メール記録(POST: APIキー認証)&履歴取得(GET: JWT認証)
|
||||
path('emails/', views.MailEmailView.as_view(), name='mail-emails'),
|
||||
|
||||
# ダッシュボード用統計(JWT認証)
|
||||
path('stats/', views.MailStatsView.as_view(), name='mail-stats'),
|
||||
|
||||
# フィードバック(認証不要、UUIDトークン)
|
||||
path('feedback/<uuid:token>/', views.FeedbackView.as_view(), name='mail-feedback'),
|
||||
|
||||
@@ -12,6 +12,7 @@ from .models import MailSender, MailEmail, MailNotificationToken
|
||||
from .serializers import (
|
||||
MailSenderSerializer,
|
||||
MailEmailCreateSerializer,
|
||||
MailEmailListSerializer,
|
||||
FeedbackDetailSerializer,
|
||||
)
|
||||
|
||||
@@ -107,13 +108,29 @@ class SenderContextView(APIView):
|
||||
})
|
||||
|
||||
|
||||
class MailEmailCreateView(APIView):
|
||||
class MailEmailView(APIView):
|
||||
"""
|
||||
POST /api/mail/emails/
|
||||
メールを記録する。llm_verdict == 'important' の場合はトークンも発行する。
|
||||
GET /api/mail/emails/ メール処理履歴を取得(JWT認証)
|
||||
POST /api/mail/emails/ メールを記録する(APIキー認証、Windmill向け)
|
||||
"""
|
||||
permission_classes = [MailAPIKeyPermission]
|
||||
authentication_classes = []
|
||||
|
||||
def get_permissions(self):
|
||||
if self.request.method == 'POST':
|
||||
return [MailAPIKeyPermission()]
|
||||
return [IsAuthenticated()]
|
||||
|
||||
def get(self, request):
|
||||
qs = MailEmail.objects.select_related('notification_token').order_by('-received_at')
|
||||
|
||||
account = request.query_params.get('account')
|
||||
if account:
|
||||
qs = qs.filter(account=account)
|
||||
verdict = request.query_params.get('verdict')
|
||||
if verdict:
|
||||
qs = qs.filter(llm_verdict=verdict)
|
||||
|
||||
serializer = MailEmailListSerializer(qs[:100], many=True)
|
||||
return Response(serializer.data)
|
||||
|
||||
def post(self, request):
|
||||
serializer = MailEmailCreateSerializer(data=request.data)
|
||||
@@ -134,6 +151,31 @@ class MailEmailCreateView(APIView):
|
||||
return Response(response_data, status=status.HTTP_201_CREATED)
|
||||
|
||||
|
||||
class MailStatsView(APIView):
|
||||
"""
|
||||
GET /api/mail/stats/ ダッシュボード用統計
|
||||
"""
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def get(self, request):
|
||||
today = timezone.now().date()
|
||||
|
||||
today_processed = MailEmail.objects.filter(received_at__date=today).count()
|
||||
today_notified = MailEmail.objects.filter(notified_at__date=today).count()
|
||||
feedback_pending = MailEmail.objects.filter(
|
||||
llm_verdict='important',
|
||||
feedback__isnull=True
|
||||
).count()
|
||||
total_rules = MailSender.objects.count()
|
||||
|
||||
return Response({
|
||||
'today_processed': today_processed,
|
||||
'today_notified': today_notified,
|
||||
'feedback_pending': feedback_pending,
|
||||
'total_rules': total_rules,
|
||||
})
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# フィードバックビュー(認証不要)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user