Skip to content
This repository was archived by the owner on Feb 9, 2026. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions backend/app/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Generated by Django 5.2.8 on 2025-11-20 12:04
# Generated by Django 5.2.9 on 2025-12-05 10:33

import django.db.models.deletion
from django.db import migrations, models
Expand Down Expand Up @@ -26,30 +26,30 @@ class Migration(migrations.Migration):
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('member_id', models.IntegerField()),
('name', models.CharField(max_length=50)),
('event_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='members', to='app.events')),
('event_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='members', to='app.events', to_field='url_end_code')),
],
),
migrations.CreateModel(
name='Payments',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('payment_id', models.IntegerField()),
('paid_by', models.IntegerField()),
('paid_for', models.IntegerField()),
('amount', models.IntegerField()),
('note', models.CharField(blank=True, max_length=200, null=True)),
('event_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='payments', to='app.events')),
('event_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='payments', to='app.events', to_field='url_end_code')),
('paid_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='payments_paid', to='app.members')),
('paid_for', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='payments_for', to='app.members')),
],
),
migrations.CreateModel(
name='Results',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('result_id', models.IntegerField()),
('paid_by', models.IntegerField()),
('paid_for', models.IntegerField()),
('amount', models.IntegerField()),
('event_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='results', to='app.events')),
('event_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='results', to='app.events', to_field='url_end_code')),
('paid_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='results_paid', to='app.members')),
('paid_for', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='results_for', to='app.members')),
],
),
]

This file was deleted.

19 changes: 0 additions & 19 deletions backend/app/migrations/0003_alter_results_event_id.py

This file was deleted.

18 changes: 13 additions & 5 deletions backend/app/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,17 @@ class Payments(models.Model): #支払いテーブル
related_name='payments',
on_delete=models.CASCADE)
payment_id = models.IntegerField()
paid_by = models.IntegerField()
paid_for = models.IntegerField()
paid_by = models.ForeignKey(Members,
on_delete=models.CASCADE,
related_name='payments_paid')
paid_for = models.ForeignKey(Members,
on_delete=models.CASCADE,
related_name='payments_for')
amount = models.IntegerField()
note = models.CharField(max_length=200, blank=True, null=True) #メモするところ

def __str__(self):
return f"Payments {self.payment_id} in {self.event_id.url_end_code}"
return f"Payments {self.payment_id}: {self.paid_by.name} -> {self.paid_for.name}"


class Results(models.Model): #結果テーブル
Expand All @@ -56,8 +60,12 @@ class Results(models.Model): #結果テーブル
related_name='results',
on_delete=models.CASCADE)
result_id = models.IntegerField()
paid_by = models.IntegerField()
paid_for = models.IntegerField()
paid_by = models.ForeignKey(Members,
on_delete=models.CASCADE,
related_name='results_paid')
paid_for = models.ForeignKey(Members,
on_delete=models.CASCADE,
related_name='results_for')
amount = models.IntegerField()

def __str__(self):
Expand Down
108 changes: 84 additions & 24 deletions backend/app/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
from django.http import JsonResponse
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt
from django.shortcuts import get_object_or_404
import json
import traceback
from channels.layers import get_channel_layer
from asgiref.sync import async_to_sync

Expand All @@ -29,35 +31,91 @@ def post(self, request, *args, **kwargs):
input_data = json.loads(request.body)
algo_id = input_data.get("algorithm_id")

group_id = input_data.get("group_id")
group_id = input_data.get("group_id") or input_data.get("event_id")

# イベント特定
event = None
if group_id:
event = models.Events.objects.filter(url_end_code=group_id).first()
if not event:
return JsonResponse({"error": "Event not found"}, status=404)

# 支払情報保存
if event:
payments_list = input_data.get("payments", [])
models.Payments.objects.filter(event_id=event).delete()

new_payments = []
for p in payments_list:
try:
payer = models.Members.objects.get(event_id=event, member_id=int(p.get("paid_by")))
receiver = models.Members.objects.get(event_id=event, member_id=int(p.get("paid_for")))

new_payments.append(models.Payments(
event_id=event,
payment_id=int(p.get("id", 0)),
paid_by=payer,
paid_for=receiver,
amount=int(p.get("amount", 0)),
note=p.get("note", "")
))
except (models.Members.DoesNotExist, ValueError):
continue

models.Payments.objects.bulk_create(new_payments)

# アルゴリズム実行部分
output_data = None

process_function = ALGORITHM_MAP.get(algo_id)

if process_function:
output_data = process_function(input_data)
else:
return JsonResponse(
{"error": f"Invalid algorithm_id: {algo_id}"}, status=400
)

# 結果保存と通知部分
if event and output_data:
models.Results.objects.filter(event_id=event).delete()

new_results = []
for r in output_data:
try:
payer = models.Members.objects.get(event_id=event, member_id=int(r.get("paid_by")))
receiver = models.Members.objects.get(event_id=event, member_id=int(r.get("paid_for")))

new_results.append(models.Results(
event_id=event,
result_id=int(r.get("id", 0)),
paid_by=payer,
paid_for=receiver,
amount=int(r.get("amount", 0))
))
except (models.Members.DoesNotExist, ValueError):
continue

models.Results.objects.bulk_create(new_results)

# WebSocketでグループ全員に通知を送る処理
if group_id:
channel_layer = get_channel_layer()
try:
channel_layer = get_channel_layer()

group_name = f'warikan_{group_id}'

async_to_sync(channel_layer.group_send)(
group_name,
{
'type': 'chat_message',
'message': {
'results': output_data
group_name = f'warikan_{group_id}'

async_to_sync(channel_layer.group_send)(
group_name,
{
'type': 'chat_message',
'message': {
'results': output_data
}
}
}
)

else:
return JsonResponse(
{"error": f"Invalid algorithm_id: {algo_id}"}, status=400
)
)
except Exception as ws_error:
print(f"Warning: WebSocket通知に失敗: {ws_error}")

return JsonResponse(output_data, safe=False)

Expand Down Expand Up @@ -93,7 +151,7 @@ def get_queryset(self):
qs = super().get_queryset()
event_code = self.request.query_params.get("event_id")
if event_code:
qs = qs.filter(event_id=event_code)
qs = qs.filter(event_id__url_end_code=event_code)
return qs

@action(detail=False, methods=["delete"])
Expand All @@ -102,7 +160,7 @@ def delete_by_key(self, request):
member_id = request.query_params.get("member_id")

deleted, _ = models.Members.objects.filter(
event_id=event_id,
event_id__url_end_code=event_id,
member_id=member_id
).delete()

Expand All @@ -117,7 +175,7 @@ def get_queryset(self):
qs = super().get_queryset()
event_code = self.request.query_params.get("event_id")
if event_code:
qs = qs.filter(event_id=event_code)
qs = qs.filter(event_id__url_end_code=event_code)
return qs

@action(detail=False, methods=["delete"])
Expand All @@ -126,7 +184,7 @@ def delete_by_key(self, request):
payment_id = request.query_params.get("payment_id")

deleted, _ = models.Payments.objects.filter(
event_id=event_id,
event_id__url_end_code=event_id,
payment_id=payment_id
).delete()

Expand All @@ -138,7 +196,9 @@ def patch_by_key(self, request):
payment_id = request.data.get("payment_id")

try:
obj = models.Payments.objects.get(event_id=event_id, payment_id=payment_id)
obj = models.Payments.objects.get(
event_id__url_end_code=event_id,
payment_id=payment_id)
except models.Payments.DoesNotExist:
return Response({"error": "payment not found"}, status=404)

Expand All @@ -157,13 +217,13 @@ def get_queryset(self):
qs = super().get_queryset()
event_code = self.request.query_params.get("event_id")
if event_code:
qs = qs.filter(event_id=event_code)
qs = qs.filter(event_id__url_end_code=event_code)
return qs

@action(detail=False, methods=["delete"])
def delete_by_event(self, request):
event_id = request.query_params.get("event_id")

deleted, _ = models.Results.objects.filter(event_id=event_id).delete()
deleted, _ = models.Results.objects.filter(event_id__url_end_code=event_id).delete()
return Response({"status": "deleted", "count": deleted}, status=200)

4 changes: 3 additions & 1 deletion frontend/src/lib/fetchResult.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { toCamelCaseObject, toSnakeCaseObject } from "./caseConvert";
interface FetchResultParams {
algorithmId: number;
payments: Payment[];
eventId: string;
}

/**
Expand All @@ -25,10 +26,11 @@ async function fetchResult(params: FetchResultParams): Promise<Result[]> {
const url = `${apiEndpoint}/api/v1/calculate/`;

// リクエストボディをsnake_caseに変換
// API仕様に従って、algorithm_idとpaymentsを送信
// API仕様に従って、algorithm_idとpaymentsとevent_idを送信
const requestBody = toSnakeCaseObject({
algorithmId: params.algorithmId,
payments: params.payments,
eventId: params.eventId,
});

try {
Expand Down
Loading