feat: ofm 完善1
This commit is contained in:
parent
31f4e2869d
commit
ab22415e0f
|
@ -0,0 +1,14 @@
|
|||
from django_filters import rest_framework as filters
|
||||
from apps.ofm.models import MroomBooking
|
||||
|
||||
|
||||
class MroomBookingFilterset(filters.FilterSet):
|
||||
class Meta:
|
||||
model = MroomBooking
|
||||
fields = {
|
||||
'slot_b__mroom': ['exact', 'in'],
|
||||
'slot_b__booking': ['exact'],
|
||||
'slot_b__mdate': ['exact', 'gte', 'lte'],
|
||||
'create_by': ['exact'],
|
||||
"id": ["exact"]
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
# Generated by Django 3.2.12 on 2025-06-25 09:29
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Mroom',
|
||||
fields=[
|
||||
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
|
||||
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
|
||||
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
|
||||
('name', models.CharField(max_length=50, unique=True, verbose_name='会议室名称')),
|
||||
('location', models.CharField(max_length=100, verbose_name='位置')),
|
||||
('capacity', models.PositiveIntegerField(verbose_name='容纳人数')),
|
||||
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mroom_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
|
||||
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mroom_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='MroomBooking',
|
||||
fields=[
|
||||
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
|
||||
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
|
||||
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
|
||||
('title', models.CharField(max_length=100, verbose_name='会议主题')),
|
||||
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mroombooking_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
|
||||
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mroombooking_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='MroomSlot',
|
||||
fields=[
|
||||
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
|
||||
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
|
||||
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
|
||||
('mdate', models.DateField(db_index=True, verbose_name='会议日期')),
|
||||
('slot', models.PositiveIntegerField(help_text='0-47', verbose_name='时段')),
|
||||
('booking', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='slot_b', to='ofm.mroombooking')),
|
||||
('mroom', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='slot_m', to='ofm.mroom')),
|
||||
],
|
||||
options={
|
||||
'unique_together': {('mroom', 'mdate', 'slot')},
|
||||
},
|
||||
),
|
||||
]
|
|
@ -15,8 +15,8 @@ class MroomBooking(CommonADModel):
|
|||
|
||||
class MroomSlot(BaseModel):
|
||||
"""TN: 会议室时段"""
|
||||
mroom = models.ForeignKey(Mroom, on_delete=models.CASCADE)
|
||||
booking = models.ForeignKey(MroomBooking, on_delete=models.CASCADE)
|
||||
mroom = models.ForeignKey(Mroom, on_delete=models.CASCADE, related_name="slot_m")
|
||||
booking = models.ForeignKey(MroomBooking, on_delete=models.CASCADE, related_name="slot_b")
|
||||
mdate = models.DateField('会议日期', db_index=True)
|
||||
slot = models.PositiveIntegerField('时段', help_text='0-47')
|
||||
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
from .models import Mroom, MroomBooking, MroomSlot
|
||||
from apps.utils.serializers import CustomModelSerializer
|
||||
from rest_framework import serializers
|
||||
from django.db import transaction
|
||||
from rest_framework.exceptions import ParseError
|
||||
from apps.utils.constants import EXCLUDE_FIELDS
|
||||
|
||||
|
||||
class MroomSerializer(CustomModelSerializer):
|
||||
class Meta:
|
||||
model = Mroom
|
||||
fields = '__all__'
|
||||
|
||||
|
||||
class MroomBookingSerializer(CustomModelSerializer):
|
||||
mroom = serializers.PrimaryKeyRelatedField(queryset=Mroom.objects.all(), write_only=True, label="会议室")
|
||||
mdate = serializers.DateField(write_only=True, label="预订日期")
|
||||
slots = serializers.ListField(child=serializers.IntegerField(), write_only=True, label="时段索引")
|
||||
create_by_name = serializers.CharField(source='create_by.username', read_only=True)
|
||||
class Meta:
|
||||
model = MroomBooking
|
||||
fields = '__all__'
|
||||
read_only_fields = EXCLUDE_FIELDS
|
||||
|
||||
@transaction.atomic
|
||||
def create(self, validated_data):
|
||||
mroom = validated_data.pop('mroom')
|
||||
slots = validated_data.pop('slots')
|
||||
mdate = validated_data.pop('mdate')
|
||||
booking = MroomBooking.objects.create(**validated_data)
|
||||
MroomSlot.objects.filter(booking=booking).delete()
|
||||
for slot in slots:
|
||||
if slot < 0 or slot > 47:
|
||||
raise ParseError("时段索引超出范围")
|
||||
try:
|
||||
MroomSlot.objects.create(booking=booking, slot=slot, mdate=mdate, mroom=mroom)
|
||||
except Exception as e:
|
||||
raise ParseError(f"时段已预订,请刷新重选-{e}")
|
||||
return booking
|
||||
|
||||
@transaction.atomic
|
||||
def update(self, instance, validated_data):
|
||||
mroom = validated_data.pop('mroom')
|
||||
slots = validated_data.pop('slots')
|
||||
mdate = validated_data.pop('mdate')
|
||||
booking = super().update(instance, validated_data)
|
||||
MroomSlot.objects.filter(booking=instance).delete()
|
||||
for slot in slots:
|
||||
if slot < 0 or slot > 47:
|
||||
raise ParseError("时段索引超出范围")
|
||||
try:
|
||||
MroomSlot.objects.create(booking=booking, slot=slot, mdate=mdate, mroom=mroom)
|
||||
except Exception as e:
|
||||
raise ParseError(f"时段已预订,请刷新重选-{e}")
|
||||
return booking
|
||||
|
||||
|
||||
|
||||
class MroomSlotSerializer(CustomModelSerializer):
|
||||
booking_title = serializers.CharField(source='booking.title', read_only=True)
|
||||
class Meta:
|
||||
model = MroomSlot
|
||||
fields = '__all__'
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
from django.urls import path, include
|
||||
from rest_framework.routers import DefaultRouter
|
||||
from apps.ofm.views import (MroomViewSet, MroomBookingViewSet, MroomSlotViewSet)
|
||||
|
||||
API_BASE_URL = 'api/ofm/'
|
||||
HTML_BASE_URL = 'dhtml/ofm/'
|
||||
|
||||
router = DefaultRouter()
|
||||
router.register('mroom', MroomViewSet, basename='mroom')
|
||||
router.register('mroombooking', MroomBookingViewSet, basename='mroombooking')
|
||||
router.register('mroomslot', MroomSlotViewSet, basename='mroomslot')
|
||||
urlpatterns = [
|
||||
path(API_BASE_URL, include(router.urls)),
|
||||
]
|
|
@ -1,4 +1,97 @@
|
|||
from django.shortcuts import render
|
||||
from apps.utils.viewsets import CustomModelViewSet
|
||||
from apps.utils.viewsets import CustomModelViewSet, CustomGenericViewSet
|
||||
from .models import Mroom, MroomBooking, MroomSlot
|
||||
from .serializers import MroomSerializer, MroomBookingSerializer, MroomSlotSerializer
|
||||
from rest_framework.decorators import action
|
||||
from apps.utils.mixins import CustomListModelMixin
|
||||
from rest_framework.exceptions import ParseError
|
||||
from apps.ofm.filters import MroomBookingFilterset
|
||||
|
||||
|
||||
class MroomViewSet(CustomModelViewSet):
|
||||
"""list: 会议室
|
||||
|
||||
会议室
|
||||
"""
|
||||
queryset = Mroom.objects.all()
|
||||
serializer_class = MroomSerializer
|
||||
|
||||
|
||||
class MroomBookingViewSet(CustomModelViewSet):
|
||||
"""list: 会议室预订
|
||||
|
||||
会议室预订
|
||||
"""
|
||||
queryset = MroomBooking.objects.all()
|
||||
serializer_class = MroomBookingSerializer
|
||||
select_related_fields = ["create_by"]
|
||||
filterset_class = MroomBookingFilterset
|
||||
|
||||
def add_info_for_list(self, data):
|
||||
booking_ids = [d["id"] for d in data]
|
||||
slots = MroomSlot.objects.filter(booking__in=booking_ids).order_by("booking", "mroom", "mdate", "slot")
|
||||
booking_info = {}
|
||||
for slot in slots:
|
||||
booking_id = slot.booking.id
|
||||
|
||||
if booking_id not in booking_info:
|
||||
booking_info[booking_id] = {
|
||||
"mdate": slot.mdate.strftime("%Y-%m-%d"), # 格式化日期
|
||||
"mroom": slot.mroom.id,
|
||||
"mroom_name": slot.mroom.name, # 会议室名称
|
||||
"time_ranges": [], # 存储时间段(如 ["8:00-9:00", "10:00-11:30"])
|
||||
"current_slots": [], # 临时存储连续的slot(用于合并)
|
||||
}
|
||||
|
||||
# 检查是否连续(当前slot是否紧接上一个slot)
|
||||
current_slots = booking_info[booking_id]["current_slots"]
|
||||
if not current_slots or slot.slot == current_slots[-1] + 1:
|
||||
current_slots.append(slot.slot)
|
||||
else:
|
||||
# 如果不连续,先把当前连续的slot转换成时间段
|
||||
if current_slots:
|
||||
start_time = self._slot_to_time(current_slots[0])
|
||||
end_time = self._slot_to_time(current_slots[-1] + 1)
|
||||
booking_info[booking_id]["time_ranges"].append(f"{start_time}-{end_time}")
|
||||
current_slots.clear()
|
||||
current_slots.append(slot.slot)
|
||||
|
||||
# 处理最后剩余的连续slot
|
||||
for info in booking_info.values():
|
||||
if info["current_slots"]:
|
||||
start_time = self._slot_to_time(info["current_slots"][0])
|
||||
end_time = self._slot_to_time(info["current_slots"][-1] + 1)
|
||||
info["time_ranges"].append(f"{start_time}-{end_time}")
|
||||
info.pop("current_slots") # 清理临时数据
|
||||
|
||||
for item in data:
|
||||
item.update(booking_info.get(item["id"], {}))
|
||||
return data
|
||||
|
||||
@staticmethod
|
||||
def _slot_to_time(slot):
|
||||
"""将slot (0-47) 转换为 HH:MM 格式的时间字符串"""
|
||||
hours = slot // 2
|
||||
minutes = (slot % 2) * 30
|
||||
return f"{hours:02d}:{minutes:02d}"
|
||||
|
||||
def perform_update(self, serializer):
|
||||
ins:MroomBooking = self.get_object()
|
||||
if ins.create_by != self.request.user:
|
||||
raise ParseError("只允许创建者修改")
|
||||
return super().perform_update(serializer)
|
||||
|
||||
def perform_destroy(self, instance):
|
||||
if instance.create_by != self.request.user:
|
||||
raise ParseError("只允许创建者删除")
|
||||
return super().perform_destroy(instance)
|
||||
|
||||
|
||||
class MroomSlotViewSet(CustomListModelMixin, CustomGenericViewSet):
|
||||
"""list: 会议室预订时段
|
||||
|
||||
会议室预订时段
|
||||
"""
|
||||
queryset = MroomSlot.objects.all()
|
||||
serializer_class = MroomSlotSerializer
|
||||
filterset_fields = ["mroom", "mdate", "booking"]
|
|
@ -84,7 +84,8 @@ INSTALLED_APPS = [
|
|||
'apps.dpm',
|
||||
'apps.cm',
|
||||
'apps.cms',
|
||||
'apps.wpmw'
|
||||
'apps.wpmw',
|
||||
'apps.ofm'
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
|
|
|
@ -74,6 +74,7 @@ urlpatterns = [
|
|||
path('', include('apps.cm.urls')),
|
||||
path('', include('apps.cms.urls')),
|
||||
path('', include('apps.wpmw.urls')),
|
||||
path('', include('apps.ofm.urls')),
|
||||
|
||||
# 前端页面入口
|
||||
path('', TemplateView.as_view(template_name="index.html")),
|
||||
|
|
Loading…
Reference in New Issue