Browse Source

roomumy代码

linhaohong 4 months ago
parent
commit
50c211c9c0

+ 22 - 0
Ansjer/cn_config/formal_settings.py

@@ -94,6 +94,13 @@ SERVER_HOST3 = '192.168.0.51'
 DATABASES_USER3 = 'root'
 DATABASES_PASS3 = 'Ansjer123'
 
+# 袋鼠妈妈数据库
+DATABASE_DATA4 = 'pro_roomumy'
+SERVER_HOST4 = '192.168.0.51'
+DATABASES_USER4 = 'root'
+DATABASES_PASS4 = 'Ansjer123'
+
+
 DATABASES = {
     'default': {
         'ENGINE': 'django.db.backends.mysql',
@@ -138,6 +145,21 @@ DATABASES = {
             'use_unicode': True,
             'init_command': "SET sql_mode='STRICT_TRANS_TABLES'"
         }
+    },
+    'mysql04': {
+        'ENGINE': 'django.db.backends.mysql',
+        'NAME': DATABASE_DATA4,
+        'USER': DATABASES_USER4,
+        'PASSWORD': DATABASES_PASS4,
+        'HOST': SERVER_HOST4,
+        'PORT': '3306',
+        'AUTOCOMMIT': True,
+        'CONN_MAX_AGE': 60,
+        'OPTIONS': {
+            'charset': 'utf8mb4',
+            'use_unicode': True,
+            'init_command': "SET sql_mode='STRICT_TRANS_TABLES'"
+        }
     }
 }
 DATABASE_ROUTERS = ['Ansjer.database_router.DatabaseAppsRouter']

+ 2 - 0
Ansjer/urls.py

@@ -391,6 +391,8 @@ urlpatterns = [
     re_path('open/device/configuration/(?P<operation>.*)', DeviceVersionInfoController.DeviceVersionInfoView.as_view()),
     # 获取APN配置信息
     re_path('APNConfig/(?P<operation>.*)', APNConfigController.APNConfigView.as_view()),
+    # roomumy
+    re_path(r'^roomumy/', include("Roomumy.server_urls.roomumy_url")),
 
 
     # 后台界面接口 -----------------------------------------------------

+ 10 - 0
Object/Enums/RTCEnum.py

@@ -0,0 +1,10 @@
+# @Author    : Rocky
+# @File      : RTCEnum.py
+# @Time      : 2025/3/14 10:32
+from enum import Enum, unique
+
+
+@unique
+class RTCConfigEnum(Enum):
+    AppID = 'a'
+    AppKey = 'b'

+ 221 - 0
Roomumy/Controller/BabyController.py

@@ -0,0 +1,221 @@
+# -*- encoding: utf-8 -*-
+"""
+@File    : BabyController.py
+@Time    : 2024-10-7 14:37:30
+@Author  : peng
+@Email   :
+@Software: PyCharm
+"""
+
+import time
+
+from django.db import transaction
+from django.views import View
+from Ansjer.cn_config.config_formal import SECRET_ACCESS_KEY, ACCESS_KEY_ID, REGION_NAME
+from Model.models import Device_Info
+
+from Object.AWS.AmazonS3Util import AmazonS3Util
+from Roomumy.models import BabyUser, BabyGrowthStandard, DevelopmentalPoints
+from Service.CommonService import CommonService
+from Ansjer.config import LOGGER
+
+
+class BabyView(View):
+    def get(self, request, *args, **kwargs):
+        request.encoding = 'utf-8'
+        operation = kwargs.get('operation')
+        return self.validation(request.GET, request, operation)
+
+    def post(self, request, *args, **kwargs):
+        request.encoding = 'utf-8'
+        operation = kwargs.get('operation')
+        return self.validation(request.POST, request, operation)
+
+    def validation(self, request_dict, request, operation):
+        token_code, user_id, response = CommonService.verify_token_get_user_id(request_dict, request)
+        if token_code != 0:
+            return response.json(token_code)
+        if operation == 'addOrEdit':  # 添加修改宝宝信息
+            return self.add_or_edit_baby(request, request_dict, user_id, response)
+        elif operation == 'query':  # 获取宝宝信息
+            return self.query_baby_info(request_dict, user_id, response)
+        elif operation == 'delete':  # 删除宝宝信息
+            return self.delete_baby(request_dict, user_id, response)
+        elif operation == 'getDevelopmentalPoints':  # 获取发育要点
+            return self.get_developmental_points(request_dict, response)
+        else:
+            return response.json(414)
+
+    @classmethod
+    def add_or_edit_baby(cls, request, request_dict, user_id, response):
+        """
+        添加或修改宝宝信息
+        @param request_dict: 请求参数
+        @param request: 请求
+        @param user_id: 用户id
+        @request_dict serial_no: 设备序列号
+        @request_dict baby_id: 宝宝id
+        @request_dict nick_name: 昵称
+        @request_dict sex: 性别
+        @request_dict birthday: 生日
+        @request_dict icon: 头像
+        @request_dict is_default: 是否默认
+        @param response: 响应对象
+        @return: response
+        """
+        serial_no = request_dict.get('serial_no', '')
+        baby_id = request_dict.get('baby_id', None)
+        nick_name = request_dict.get('nick_name', None)
+        sex = request_dict.get('sex', None)
+        birthday = request_dict.get('birthday', None)
+        icon = request.FILES.get('icon', None)
+        is_default = request_dict.get('is_default', 0)
+        if not all([nick_name, sex, birthday]):
+            return response.json(404)
+        now_time = int(time.time())
+        bucket = "ansjerfilemanager"
+        s3_url = 'https://{}.s3.{}.amazonaws.com.cn/'.format(bucket, REGION_NAME)
+        LOGGER.info(f"添加或修改宝宝信息打印参数{request_dict}, user_id:{user_id},icon:{icon}")
+        try:
+            with transaction.atomic():
+                device_id = ""
+                if serial_no:
+                    device_info_qs = Device_Info.objects.filter(userID=user_id, serial_number=serial_no)
+                    if not device_info_qs.exists():
+                        return response.json(14)
+                    device_id = CommonService.query_uid_with_serial(serial_no)
+                if baby_id:
+                    BabyUser.objects.filter(id=baby_id, user_id=user_id).update(is_default=is_default,
+                                                                                birthday=birthday,
+                                                                                nick_name=nick_name, sex=sex,
+                                                                                device_id=device_id,
+                                                                                serial_no=serial_no,
+                                                                                updated_time=now_time)
+                else:
+                    if sex == '1':
+                        icon_url = s3_url + 'Roomumy/boy.jpg'
+                    else:
+                        icon_url = s3_url + 'Roomumy/girl.jpg'
+                    baby_qs = BabyUser.objects.create(user_id=user_id, is_default=is_default, birthday=birthday,
+                                                      nick_name=nick_name, sex=sex, device_id=device_id, serial_no=serial_no,
+                                                      created_time=now_time, updated_time=now_time, icon_url=icon_url)
+                    baby_id = baby_qs.id
+                if icon:
+                    s3_obj = AmazonS3Util(ACCESS_KEY_ID, SECRET_ACCESS_KEY, REGION_NAME)
+                    icon_path = 'Roomumy/icon_{}.jpg'.format(baby_id)
+                    s3_obj.upload_file_obj(
+                        bucket,
+                        icon_path,
+                        icon,
+                        {"ContentType": icon.content_type, "ACL": "public-read"},
+                    )
+                    icon_url = s3_url + icon_path
+                    BabyUser.objects.filter(id=baby_id, user_id=user_id).update(icon_url=icon_url)
+                if is_default == '1':
+                    BabyUser.objects.filter(user_id=user_id).exclude(id=baby_id).update(is_default=False)
+            return response.json(0)
+        except Exception as e:
+            return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
+
+    @classmethod
+    def query_baby_info(cls, request_dict, user_id, response):
+        """
+        获取宝宝信息
+        """
+        baby_id = request_dict.get('baby_id', None)
+
+        try:
+            baby_qs = BabyUser.objects.filter(user_id=user_id)
+            if baby_id:
+                baby_qs = baby_qs.filter(id=baby_id)
+            count = baby_qs.count()
+            baby_list = list(
+                baby_qs.values('id', 'nick_name', 'sex', 'birthday', 'icon_url', 'device_id', 'is_default', 'serial_no'))
+
+            current_time = int(time.time())  # 获取当前时间戳
+
+            for baby in baby_list:
+                birthday = baby.get('birthday', 0)
+                baby['growth_standard'] = None  # 初始化字段
+
+                # 仅处理有效生日数据
+                if birthday > 0:
+                    age_days = (current_time - birthday) // 86400  # 计算年龄天数
+
+                    # 查询成长标准数据
+                    standards = BabyGrowthStandard.objects.filter(
+                        age_min__lte=age_days,
+                        age_max__gte=age_days,
+                        gender=baby.get('sex', 0)  # 默认性别为女
+                    ).first()  # 直接获取第一条匹配记录
+
+                    if standards:
+                        baby['growth_standard'] = {
+                            'height': {'min': standards.height_min, 'max': standards.height_max},
+                            'weight': {'min': standards.weight_min, 'max': standards.weight_max},
+                            'head_circumference': {
+                                'min': standards.head_circumference_min,
+                                'max': standards.head_circumference_max
+                            }
+                        }
+                    else:
+                        baby['growth_standard'] = {
+                            'height': {'min': None, 'max': None},
+                            'weight': {'min': None, 'max': None},
+                            'head_circumference': {'min': None, 'max': None}
+                        }
+            return response.json(0, {'total': count, 'baby_list': baby_list})
+        except Exception as e:
+            return response.json(500, f'error_line:{e.__traceback__.tb_lineno}, error_msg:{repr(e)}')
+
+    @classmethod
+    def delete_baby(cls, request_dict, user_id, response):
+        """
+        删除宝宝信息
+        @param request_dict: 请求参数
+        @param user_id: 用户id
+        @request_dict baby_id: 宝宝id
+        @param response: 响应对象
+        @return: response
+        """
+        baby_id = request_dict.get('baby_id', None)
+
+        try:
+            BabyUser.objects.filter(user_id=user_id, id=baby_id).delete()
+            return response.json(0)
+        except Exception as e:
+            return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
+
+    @staticmethod
+    def get_developmental_points(request_dict, response):
+        """
+        获取喂养要点
+        @param request_dict: 请求参数
+        @request_dict month: 月份信息
+        @param response: 响应对象
+        @return: response
+        """
+        month = request_dict.get('month', None)
+
+        try:
+            if not month:
+                return response.json(444)
+            developmental_points_qs = DevelopmentalPoints.objects.filter(month=month).values()
+            if not developmental_points_qs.exists():
+                return response.json(173)
+            title = developmental_points_qs[0]['title']
+            weight = developmental_points_qs[0]['weight']
+            height = developmental_points_qs[0]['height']
+            head_circumference = developmental_points_qs[0]['head_circumference']
+            content = developmental_points_qs[0]['content']
+
+            res = {
+                'title': title,
+                'weight': weight,
+                'height': height,
+                'head_circumference': head_circumference,
+                'content': content
+            }
+            return response.json(0, res)
+        except Exception as e:
+            return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))

+ 322 - 0
Roomumy/Controller/FeedDiaryController.py

@@ -0,0 +1,322 @@
+# -*- encoding: utf-8 -*-
+"""
+@File    : FeedDiaryController.py
+@Time    : 2024-10-7 14:37:30
+@Author  : peng
+@Email   :
+@Software: PyCharm
+"""
+from datetime import datetime, timedelta
+import time
+
+from django.views import View
+
+from Roomumy.models import FeedDiary, FeedType
+from Service.CommonService import CommonService
+
+
+class FeedDiaryView(View):
+    def get(self, request, *args, **kwargs):
+        request.encoding = 'utf-8'
+        operation = kwargs.get('operation')
+        return self.validation(request.GET, request, operation)
+
+    def post(self, request, *args, **kwargs):
+        request.encoding = 'utf-8'
+        operation = kwargs.get('operation')
+        return self.validation(request.POST, request, operation)
+
+    def validation(self, request_dict, request, operation):
+        token_code, user_id, response = CommonService.verify_token_get_user_id(request_dict, request)
+        if token_code != 0:
+            return response.json(token_code)
+        if operation == 'query':  # 查询喂养记录
+            return self.get_feed_diary_data(request_dict, response)
+        elif operation == 'queryStatistics':  # 查询喂养统计
+            return self.get_statistics_data(request_dict, response)
+        elif operation == 'addOrEdit':  # 添加或编辑喂养日记
+            return self.add_or_edit_feed_diary(request_dict, response)
+        elif operation == 'delete':  # 删除喂养日记
+            return self.delete_feed_diary(request_dict, response)
+        elif operation == 'getFeedList':  # 查询喂养类型列表
+            return self.get_feed_list(request_dict, response)
+        elif operation == 'feedTypeConfig':
+            return self.feed_type_config(request_dict, response)
+        elif operation == 'getDailyRecordStatus':  # 查询天数记录
+            return self.get_daily_record_status(request_dict, response)
+        else:
+            return response.json(414)
+
+    @classmethod
+    def get_feed_diary_data(cls, request_dict, response):
+        """
+        查询喂养记录
+        @param request_dict: 请求参数
+        @request_dict start_time: 开始时间戳
+        @request_dict end_time: 结束时间戳
+        @request_dict baby_id: 宝宝id
+        @request_dict event_type: 喂养类型
+        @param response: 响应对象
+        @return: response
+        """
+        start_time = request_dict.get('start_time', None)
+        end_time = request_dict.get('end_time', None)
+        baby_id = request_dict.get('baby_id', None)
+        feed_type = request_dict.get('feed_type', None)
+        if not all([start_time, end_time, baby_id]):
+            return response.json(444, {'error param': 'start_time, end_time or baby_id'})
+        try:
+            feed_diary = FeedDiary.objects.filter(date_time__gte=start_time, date_time__lte=end_time,
+                                                  baby_id=baby_id)
+            if feed_type:
+                feed_diary = feed_diary.filter(feed_type=feed_type)
+            feed_diary = feed_diary.values('feed_type__name', 'feed_type__icon_url', 'feed_content', 'date_time',
+                                           'id', 'feed_type')
+            feed_diary = list(feed_diary)
+
+            # 统计各类型的喂养次数
+            feed_statistics = {}
+            for item in feed_diary:
+                feed_type_id = item['feed_type']
+                feed_type_name = item['feed_type__name']
+
+                # 初始化统计项
+                if feed_type_id not in feed_statistics:
+                    feed_statistics[feed_type_id] = {
+                        "feed_type_name": feed_type_name,
+                        "frequency": 0,
+                        "total": 0  # 仅对类型1/2/3有效
+                    }
+
+                # 更新统计
+                feed_statistics[feed_type_id]["frequency"] += 1
+
+                # 仅对类型1/2/3计算total
+                if item['feed_type'] in [1, 2, 3] and "total" in item.get("feed_content", {}):
+                    feed_statistics[feed_type_id]["total"] += item["feed_content"]["total"]
+
+            # 转换统计结果为列表
+            feed_statistics_list = [
+                {
+                    "feed_type": feed_type_id,
+                    "feed_type_name": data["feed_type_name"],
+                    "frequency": str(data["frequency"]),
+                    "total": str(data["total"])
+                }
+                for feed_type_id, data in feed_statistics.items()
+            ]
+
+            return response.json(0, {
+                'feed_diary': feed_diary,
+                'feed_statistics': feed_statistics_list  # 改为列表形式,更清晰
+            })
+        except Exception as e:
+            return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
+
+    @classmethod
+    def get_statistics_data(cls, request_dict, response):
+        """
+        查询喂养记录
+        @param request_dict: 请求参数
+        @request_dict start_time: 开始时间戳
+        @request_dict end_time: 结束时间戳
+        @request_dict baby_id: 宝宝id
+        @request_dict event_type: 喂养类型
+        @request_dict sub_event_type: 喂养子类型
+        @param response: 响应对象
+        @return: response
+        """
+        start_time = request_dict.get('start_time', None)
+        end_time = request_dict.get('end_time', None)
+        baby_id = request_dict.get('baby_id', None)
+        feed_type = request_dict.get('feed_type', None)
+        sub_feed_type = request_dict.get('sub_feed_type', None)
+        if not all([start_time, end_time, baby_id, feed_type]):
+            return response.json(444, {'error param': 'start_time, end_time, event_type or baby_id'})
+        s_time = datetime.datetime.fromtimestamp(int(start_time))
+        e_time = datetime.datetime.fromtimestamp(int(end_time))
+        time_list = CommonService.cutting_time(s_time, e_time, 'day')
+        try:
+            result_list = []
+            for time_range in time_list:
+                content_list = []
+                feed_diary = FeedDiary.objects.filter(created_time__gte=time_range[0], created_time__lt=time_range[1],
+                                                      baby_id=baby_id, feed_type_id=feed_type).values_list(
+                    'feed_content', flat=True)
+                count = 0
+                if feed_type == '5' and sub_feed_type:  # 换尿布
+                    sub_feed_type = int(sub_feed_type)
+                    for item in feed_diary:
+                        if item['sub_feed_type'] in [sub_feed_type, 3]:  # 1:嘘嘘 2:臭臭 3:嘘嘘+臭臭 4:干爽
+                            count += 1
+                            content_list.append(item)
+                else:
+                    count = feed_diary.count()
+                    content_list = list(feed_diary)
+                result_list.append({
+                    'date_time': time_range[0],
+                    'count': count,
+                    'feed_content': content_list
+                })
+            return response.json(0, result_list)
+        except Exception as e:
+            return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
+
+    @classmethod
+    def add_or_edit_feed_diary(cls, request_dict, response):
+        """
+        添加编辑喂养记录
+        @param request_dict: 请求参数
+        @request_dict date_time: 喂养时间戳
+        @request_dict baby_id: 宝宝id
+        @request_dict feed_id: 记录id
+        @request_dict event_type: 喂养类型
+        @request_dict event_content: 喂养事件
+        @param response: 响应对象
+        @return: response
+        """
+        date_time = request_dict.get('date_time', None)
+        baby_id = request_dict.get('baby_id', None)
+        feed_id = request_dict.get('feed_id', None)
+        feed_type = request_dict.get('feed_type', None)
+        feed_content = request_dict.get('feed_content', None)
+        if not all([date_time, baby_id, feed_type, feed_content]):
+            return response.json(444, {'error param': 'date_time, feed_content, feed_type or baby_id'})
+        feed_content = eval(feed_content)
+        try:
+            now_time = int(time.time())
+            feed_type_qs = FeedType.objects.filter(id=feed_type)
+            if not feed_type_qs:
+                return response.json(444, {'error param': 'feed_type'})
+            if feed_id:
+                FeedDiary.objects.filter(id=feed_id, baby_id=baby_id).update(date_time=date_time,
+                                                                             updated_time=now_time,
+                                                                             feed_content=feed_content)
+            else:
+                FeedDiary.objects.create(baby_id=baby_id, feed_type_id=feed_type, feed_content=feed_content,
+                                         date_time=date_time, created_time=now_time, updated_time=now_time)
+            return response.json(0)
+        except Exception as e:
+            return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
+
+    @classmethod
+    def delete_feed_diary(cls, request_dict, response):
+        """
+        删除喂养记录
+        @param request_dict: 请求参数
+        @request_dict baby_id: 宝宝id
+        @request_dict feed_id: 记录id
+        @param response: 响应对象
+        @return: response
+        """
+        baby_id = request_dict.get('baby_id', None)
+        feed_id = request_dict.get('feed_id', None)
+        if not all([feed_id, baby_id]):
+            return response.json(444, {'error param': 'feed_id or baby_id'})
+        try:
+            FeedDiary.objects.filter(id=feed_id, baby_id=baby_id).delete()
+            return response.json(0)
+        except Exception as e:
+            return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
+
+    @classmethod
+    def get_feed_list(cls, request_dict, response):
+        """
+        获取喂养列表
+        @param request_dict: 请求参数
+        @param response: 响应对象
+        @return: response
+        """
+        try:
+            feed_type = FeedType.objects.filter(is_show=True).values('id', 'name', 'icon_url', 'sort').order_by('sort')
+            return response.json(0, list(feed_type))
+        except Exception as e:
+            return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
+
+    @classmethod
+    def feed_type_config(cls, request_dict, response):
+        """
+        根据喂养类型返回文本列表
+        @param request_dict: 请求参数
+        @param response: 响应对象
+        @return: response
+        """
+        try:
+            feed_type = request_dict.get('feed_type_id', None)
+            if feed_type == '4':#辅食
+                feed_type_text = [
+                    "米粉", "面条", "红薯", "山药", "南瓜",
+                    "胡萝卜", "菠菜", "番茄", "土豆", "猪肉",
+                    "猪肝", "鱼肉", "鸡肉", "牛肉", "虾",
+                    "鸡蛋", "苹果", "香蕉", "猕猴桃", "火龙果"
+                ]
+
+            elif feed_type == '5':
+                feed_type_text = ["嘘嘘", "臭臭", "嘘嘘+臭臭", "干爽"]
+
+            elif feed_type == '10':
+                feed_type_text = ["额温", "耳温", "腋温", "口温", "肛温"]
+
+            else:
+                feed_type_text = []
+            return response.json(0, {"feed_type_text": feed_type_text})
+        except Exception as e:
+            return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
+
+    @classmethod
+    def get_daily_record_status(cls, request_dict, response):
+        """
+        获取记录的日期
+        @param request_dict: 请求参数
+        @request_dict baby_id: 宝宝id
+        @request_dict timestamp: 时间戳
+        @param response: 响应对象
+        @return: response
+        """
+        baby_id = request_dict.get('baby_id')
+        start_time = request_dict.get('start_time')
+        end_time = request_dict.get('end_time')
+
+        if not all([baby_id, start_time, end_time]):
+            return response.json(444, {'error': '缺少baby_id、start_time或end_time参数'})
+
+        try:
+            # 转换为整数
+            start_time = int(start_time)
+            end_time = int(end_time)
+
+            records = FeedDiary.objects.filter(
+                baby_id=baby_id,
+                date_time__gte=start_time,
+                date_time__lte=end_time
+            ).values('date_time')
+            record_dates = {
+                datetime.fromtimestamp(r['date_time']).strftime('%Y-%m-%d')
+                for r in records
+            }
+            # 生成时间段内所有日期
+            all_dates = []
+            current_date = datetime.fromtimestamp(start_time).date()
+            end_date = datetime.fromtimestamp(end_time).date()
+
+            while current_date <= end_date:
+                all_dates.append(current_date.strftime('%Y-%m-%d'))
+                current_date += timedelta(days=1)
+
+            # 构建返回结果
+            result = []
+            for date in all_dates:
+                result.append({
+                    'date': date,
+                    'has_record': date in record_dates
+                })
+
+            return response.json(0, {
+                'baby_id': baby_id,
+                'days': result
+            })
+
+        except ValueError as e:
+            return response.json(444, {'error': '时间戳格式不正确'})
+        except Exception as e:
+            return response.json(500, {'error': f'服务器错误: {str(e)}'})

+ 76 - 0
Roomumy/Controller/RTCController.py

@@ -0,0 +1,76 @@
+# @Author    : Rocky
+# @File      : RTCController.py
+# @Time      : 2025/3/14 8:49
+import random
+import time
+from hashlib import sha1
+import hmac
+import base64
+import binascii
+from optparse import OptionParser
+from django.views.generic.base import View
+from Model.models import Device_Info
+from Object.Enums.RTCEnum import RTCConfigEnum
+from Service.CommonService import CommonService
+
+
+class RTCView(View):
+    def get(self, request, *args, **kwargs):
+        request.encoding = 'utf-8'
+        operation = kwargs.get('operation')
+        return self.validation(request.GET, request, operation)
+
+    def post(self, request, *args, **kwargs):
+        request.encoding = 'utf-8'
+        operation = kwargs.get('operation')
+        return self.validation(request.POST, request, operation)
+
+    def validation(self, request_dict, request, operation):
+        token_code, user_id, response = CommonService.verify_token_get_user_id(request_dict, request)
+        if token_code != 0:
+            return response.json(token_code)
+        if operation == 'getToken':  # 获取token
+            return self.get_token(request_dict, response)
+
+    @classmethod
+    def get_token(cls, request_dict, response):
+        """
+        获取token
+        https://cloud.baidu.com/doc/RTC/s/Qjxbh7jpu#%E5%9C%A8appserver%E4%B8%8A%E9%83%A8%E7%BD%B2token%E7%94%9F%E6%88%90%E6%9C%8D%E5%8A%A1
+        @param request_dict:
+        @param response:
+        @return: res
+        """
+        uid = request_dict.get('uid', None)
+        if not uid:
+            return response.json(444)
+        try:
+            # 查询主用户user id
+            device_info_qs = Device_Info.objects.filter(UID=uid).values('vodPrimaryUserID')
+            if not device_info_qs.exists():
+                return response.json(173)
+            pri_user_id = device_info_qs[0]['vodPrimaryUserID']
+            # 签名
+            now_time = int(time.time())
+            expect_time = 999999999
+            random_str = cls.generate_random_str()
+            data = "ACS{}{}{}{}{}{}".format(RTCConfigEnum.AppID.value, now_time, random_str, uid, pri_user_id, expect_time)
+            signature = hmac.new(RTCConfigEnum.AppKey.value, data, sha1).digest()  # .encode('base64').rstrip()
+            signature = binascii.b2a_hex(signature)
+            token = '{}{}{}{}{}'.format('004', signature, now_time, random_str, expect_time)
+            res = {
+                'token': token
+            }
+            return response.json(0, res)
+        except Exception as e:
+            return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
+
+    @staticmethod
+    def generate_random_str():
+        # 生成一个随机整数
+        random_int = random.randint(0, 2 ** 32 - 1)  # 生成一个 0 到 2^32 - 1 的随机整数
+        # 将整数转换为 16 进制字符串
+        hex_string = hex(random_int)[2:]  # 去掉前缀 '0x'
+        # 补齐至 8 位
+        random_str = hex_string.zfill(8)
+        return random_str

+ 337 - 0
Roomumy/Controller/TimeAlbumController.py

@@ -0,0 +1,337 @@
+import time
+from datetime import datetime
+
+import boto3
+import botocore
+from django.core.paginator import Paginator
+from django.views import View
+from obs import ObsClient
+from Ansjer.config import HUAWEICLOUD_AK, HUAWEICLOUD_SK, HUAWEICLOUD_OBS_SERVER, CONFIG_INFO, CONFIG_CN, CONFIG_TEST, \
+    AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY
+
+from Roomumy.models import TimeAlbum, AlbumMedia, TimeDiary, DiaryAlbumMediaRelation
+from Service.CommonService import CommonService
+
+
+class TimeAlbumView(View):
+    def get(self, request, *args, **kwargs):
+        request.encoding = 'utf-8'
+        operation = kwargs.get('operation')
+        return self.validation(request.GET, request, operation)
+
+    def post(self, request, *args, **kwargs):
+        request.encoding = 'utf-8'
+        operation = kwargs.get('operation')
+        return self.validation(request.POST, request, operation)
+
+    def validation(self, request_dict, request, operation):
+        token_code, user_id, response = CommonService.verify_token_get_user_id(request_dict, request)
+        if operation == 'clearAlbumMedia':  # 删除相册媒体
+            return self.clear_album_media(response)
+        else:
+            if token_code != 0:
+                return response.json(token_code)
+            if operation == 'getAlbumPic':  # 查询相册列表
+                return self.get_album_pic(user_id, request_dict, response)
+            elif operation == 'getTimeDiary':  # 获取时光日记列表
+                return self.get_time_diary(user_id, request_dict, response)
+            elif operation == 'picLiked':  # 喜欢图片
+                return self.pic_liked(user_id, request_dict, response)
+            else:
+                return response.json(414)
+
+    def get_album_pic(self, user_id, request_dict, response):
+        device_id = request_dict.get('deviceId', None)
+        start_time = request_dict.get('startTime', None)
+        end_time = request_dict.get('endTime', None)
+        page = request_dict.get('page', 1)  # 当前页码,默认为1
+        page_size = request_dict.get('pageSize', 10)  # 每页显示的记录数,默认为10
+
+        # 检查是否提供了uid,如果没有提供返回错误
+        if device_id is None:
+            return response.json(444)
+        try:
+            # 查询TimeAlbum表,按uid过滤,且如果提供了album_date,则进一步按album_date过滤
+            time_album_qs = TimeAlbum.objects.filter(device_id=device_id).order_by('-album_date')
+
+            if start_time and end_time:
+                time_album_qs = time_album_qs.filter(album_date__gte=start_time, album_date__lte=end_time)
+
+            # 如果没有找到符合条件的TimeAlbum记录,返回错误
+            if not time_album_qs.exists():
+                return response.json(0, {})
+
+            # 对TimeAlbum结果进行分页
+            paginator = Paginator(time_album_qs, page_size)
+            time_albums = paginator.page(page)  # 获取当前页的数据
+
+            time_album_list = []
+            # 对每个TimeAlbum,查询相关的AlbumMedia
+            for time_album in time_albums:
+                # 查询与当前time_album_id关联的AlbumMedia记录
+                time_album_info = []
+                album_media_qs = AlbumMedia.objects.filter(time_album_id=time_album.id).values('id', 'device_id', 'channel',
+                                                                                               'event_time', 'status',
+                                                                                               'storage_location')
+
+                if album_media_qs.exists():
+                    for album_media in album_media_qs:
+                        storage_location = album_media['storage_location']
+                        uid = album_media['device_id']
+                        channel = album_media['channel']
+                        event_time = album_media['event_time']
+                        image = self.media_url(storage_location, uid, channel, event_time)
+
+                        # 判断是否加入时光日记
+                        liked_status = 0
+                        if DiaryAlbumMediaRelation.objects.filter(
+                                album_media_id=album_media['id'],
+                                diary__user_id=user_id
+                        ).exists():
+                            liked_status = 1
+                        time_album_info.append({
+                            'picId': album_media['id'],
+                            'image': image,
+                            'likedStatus': liked_status,
+                        })
+                    time_album_list.append({
+                        'albumDate': time_album.album_date,
+                        'albumTitle': time_album.album_title,
+                        'info': time_album_info
+                    })
+
+            # 返回分页结果和数据
+            return response.json(0, {
+                'data': time_album_list,
+                'total': paginator.count,  # 总记录数
+            })
+        except Exception as e:
+            return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
+
+    def get_time_diary(self, user_id, request_dict, response):
+        start_time = request_dict.get('startTime', None)
+        end_time = request_dict.get('endTime', None)
+        page = int(request_dict.get('page', 1))
+        page_size = int(request_dict.get('pageSize', 10))
+
+        try:
+            # 基础查询
+            time_diary_qs = TimeDiary.objects.filter(user_id=user_id).order_by('-diary_date')
+
+            # 时间范围过滤
+            if start_time and end_time:
+                time_diary_qs = time_diary_qs.filter(diary_date__gte=start_time, diary_date__lte=end_time)
+
+            # 分页
+            paginator = Paginator(time_diary_qs, page_size)
+            diaries_page = paginator.get_page(page)
+
+            diary_data_list = []
+            for diary in diaries_page:
+                # 通过中间表获取关联的图片
+                relations = DiaryAlbumMediaRelation.objects.filter(diary=diary).select_related('album_media')
+
+                media_list = []
+                for relation in relations:
+                    media = relation.album_media
+                    media_list.append({
+                        'picId': media.id,
+                        'likeStatus': 1,
+                        'image': self.media_url(media.storage_location, media.device_id, media.channel,
+                                                    media.event_time)
+                    })
+
+                diary_data_list.append({
+                    'diary_date': diary.diary_date,
+                    'info': media_list
+                })
+
+            return response.json(0, {
+                'data': diary_data_list,
+                'total': paginator.count,
+            })
+
+        except Exception as e:
+            return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
+
+    @staticmethod
+    def pic_liked(user_id, request_dict, response):
+        pic_id = request_dict.get('picId', None)
+        liked_status = request_dict.get('likedStatus', None)
+
+        if not all([pic_id, liked_status]):
+            return response.json(444)
+
+        liked_status = int(liked_status)
+
+        try:
+            album_media = AlbumMedia.objects.filter(id=pic_id).first()
+            if not album_media:
+                return response.json(404, 'pic not found')
+
+            # 获取相册的日期
+            time_diary_qs = AlbumMedia.objects.filter(id=pic_id).values('time_album_id', 'event_time', 'status')
+            if not time_diary_qs.exists():
+                return response.json(173)
+
+            event_time = time_diary_qs[0]['event_time']
+            dt = datetime.fromtimestamp(event_time)
+            dt_zero = datetime(dt.year, dt.month, dt.day)
+
+            diary_date = int(dt_zero.timestamp())
+
+            # 查找或创建用户今天的 TimeDiary
+            time_diary, created = TimeDiary.objects.get_or_create(
+                user_id=user_id,
+                diary_date=diary_date,
+                defaults={
+                    'created_time': int(time.time()),
+                    'updated_time': int(time.time())
+                }
+            )
+
+            if liked_status == 1:
+                # 添加到 TimeDiary(中间表中插入)
+                DiaryAlbumMediaRelation.objects.get_or_create(
+                    diary=time_diary,
+                    album_media=album_media,
+                    defaults={
+                        'created_time': int(time.time()),
+                        'updated_time': int(time.time())
+                    }
+                )
+                time_diary_qs.update(status=2, updated_time=int(time.time()))
+
+            elif liked_status == 0:
+                # 从中间表移除
+                DiaryAlbumMediaRelation.objects.filter(diary=time_diary, album_media=album_media).delete()
+                if DiaryAlbumMediaRelation.objects.filter(diary=time_diary).count() == 0:
+                    time_diary.delete()
+                if not DiaryAlbumMediaRelation.objects.filter(album_media=album_media).exists():
+                    time_diary_qs.update(status=1, updated_time=int(time.time()))
+
+            return response.json(0)
+
+        except Exception as e:
+            return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
+
+    @staticmethod
+    def media_url(storage_location, uid, channel, event_time):
+        if storage_location == 2:
+            obj_name = f'roomumy/albumMedia/{uid}/{channel}/{event_time}.jpeg'
+            if CONFIG_INFO == CONFIG_CN or CONFIG_INFO == CONFIG_TEST:
+                aws_client = boto3.client(
+                    's3',
+                    aws_access_key_id=AWS_ACCESS_KEY_ID[0],
+                    aws_secret_access_key=AWS_SECRET_ACCESS_KEY[0],
+                    region_name='cn-northwest-1'
+                )
+            else:
+                aws_client = boto3.client(
+                    's3',
+                    aws_access_key_id=AWS_ACCESS_KEY_ID[1],
+                    aws_secret_access_key=AWS_SECRET_ACCESS_KEY[1],
+                    region_name='cn-northwest-1'
+                )
+            params = {'Key': obj_name, 'Bucket': 'ansjerfilemanager'}
+            media_url = aws_client.generate_presigned_url(
+                'get_object', Params=params, ExpiresIn=300)
+        elif storage_location == 5:
+            obj_name = f'roomumy/albumMedia/{uid}/{channel}/{event_time}.jpeg'
+            obs_client = ObsClient(
+                access_key_id=HUAWEICLOUD_AK, secret_access_key=HUAWEICLOUD_SK, server=HUAWEICLOUD_OBS_SERVER)
+            create_res = obs_client.createSignedUrl(
+                method='GET', bucketName="asj-app", objectKey=obj_name, expires=600)
+            media_url = create_res.signedUrl
+        else:
+            media_url = ''
+        return media_url
+
+
+    @staticmethod
+    def clear_album_media(response):
+        # 获取当前时间
+        current_time = int(time.time()) - 2592000
+
+        # 预加载关联的TimeAlbum对象
+        album_media_qs = AlbumMedia.objects.filter(
+            status=1,
+            event_time__lt=current_time
+        )
+
+        # 如果没有需要处理的数据,直接返回
+        if not album_media_qs.exists():
+            return response.json(0)
+
+        # 初始化 Huawei Cloud 的 ObsClient
+        obs_client = ObsClient(
+            access_key_id=HUAWEICLOUD_AK,
+            secret_access_key=HUAWEICLOUD_SK,
+            server=HUAWEICLOUD_OBS_SERVER
+        )
+
+        # 根据配置选择初始化 AWS 客户端
+        aws_client_config = {
+            'aws_access_key_id': AWS_ACCESS_KEY_ID[0] if CONFIG_INFO in [CONFIG_CN, CONFIG_TEST] else AWS_ACCESS_KEY_ID[1],
+            'aws_secret_access_key': AWS_SECRET_ACCESS_KEY[0] if CONFIG_INFO in [CONFIG_CN, CONFIG_TEST] else AWS_SECRET_ACCESS_KEY[1],
+            'region_name': 'cn-northwest-1' if CONFIG_INFO in [CONFIG_CN, CONFIG_TEST] else 'us-east-1'
+        }
+        aws_client = boto3.client('s3', **aws_client_config)
+
+        # 准备批量删除的对象
+        aws_objects_to_delete = []
+        obs_objects_to_delete = []
+        time_album_ids_to_check = set()
+        album_media_ids_to_delete = []
+
+        # 收集需要删除的对象和时间相册ID
+        for album_media in album_media_qs:
+            uid = album_media.device_id
+            event_time = album_media.event_time
+            obj_name = f'roomumy/albumMedia/{uid}/{album_media.channel}/{event_time}.jpeg'
+
+            album_media_ids_to_delete.append(album_media.id)
+
+            if album_media.time_album_id:
+                time_album_ids_to_check.add(album_media.time_album_id)
+
+            if album_media.storage_location == 2:
+                aws_objects_to_delete.append({'Key': obj_name})
+            elif album_media.storage_location == 5:
+                obs_objects_to_delete.append(obj_name)
+
+        # 批量删除AWS对象
+        if aws_objects_to_delete:
+            # AWS S3支持批量删除,最多1000个对象每次
+            for i in range(0, len(aws_objects_to_delete), 1000):
+                batch = aws_objects_to_delete[i:i + 1000]
+                aws_client.delete_objects(
+                    Bucket='ansjerfilemanager',
+                    Delete={'Objects': batch}
+                )
+
+        # 删除华为OBS对象
+        if obs_objects_to_delete:
+            for obj_name in obs_objects_to_delete:
+                obs_client.deleteObject(bucketName="asj-app", objectKey=obj_name)
+
+        # 检查需要删除的TimeAlbum
+        if time_album_ids_to_check:
+            # 查询TimeAlbum关联的AlbumMedia
+            time_albums_with_media = AlbumMedia.objects.filter(
+                time_album_id__in=time_album_ids_to_check
+            ).exclude(
+                id__in=album_media_ids_to_delete
+            ).values_list('time_album_id', flat=True).distinct()
+
+            # 找出没有关联媒体的TimeAlbum
+            time_albums_to_delete = set(time_album_ids_to_check) - set(time_albums_with_media)
+
+            # 批量删除TimeAlbum
+            if time_albums_to_delete:
+                TimeAlbum.objects.filter(id__in=time_albums_to_delete).delete()
+
+        # 处理完所有项后,删除数据库中的记录
+        album_media_qs.delete()
+
+        return response.json(0)

+ 0 - 0
Roomumy/__init__.py


+ 3 - 0
Roomumy/admin.py

@@ -0,0 +1,3 @@
+from django.contrib import admin
+
+# Register your models here.

+ 6 - 0
Roomumy/apps.py

@@ -0,0 +1,6 @@
+from django.apps import AppConfig
+
+
+class RoomumyConfig(AppConfig):
+    default_auto_field = 'django.db.models.BigAutoField'
+    name = 'Roomumy'

+ 172 - 0
Roomumy/models.py

@@ -0,0 +1,172 @@
+from django.db import models
+
+
+# Create your models here.
+class BabyUser(models.Model):
+    id = models.AutoField(primary_key=True, verbose_name='自增id')
+    user_id = models.CharField(default='', max_length=32, verbose_name='关联用户id')
+    nick_name = models.CharField(default='', max_length=20, verbose_name='昵称')
+    sex = models.SmallIntegerField(default=0, verbose_name='性别')  # 0:女 1:男
+    birthday = models.IntegerField(default=0, verbose_name='生日')
+    icon_url = models.TextField(default='', verbose_name='头像地址')
+    device_id = models.CharField(default='', max_length=32, verbose_name='关联设备')
+    serial_no = models.CharField(default='', max_length=32, verbose_name='关联序列号')
+    is_default = models.BooleanField(default=False, verbose_name='是否默认')
+    created_time = models.IntegerField(default=0, verbose_name='创建时间')
+    updated_time = models.IntegerField(default=0, verbose_name='更新时间')
+
+    class Meta:
+        db_table = 'baby_user'
+        verbose_name = '宝宝用户表'
+        verbose_name_plural = verbose_name
+        app_label = 'Roomumy'
+
+
+class FeedType(models.Model):
+    id = models.AutoField(primary_key=True, verbose_name='自增id')
+    name = models.CharField(default='', max_length=10, verbose_name='名称')
+    # 1:母乳亲喂 2:母乳瓶喂 3:奶粉 4:辅食 5:换尿布 6:身高体重 7:户外活动 8:育儿日记 9:自定义 10:体温
+    sort = models.IntegerField(default=0, verbose_name='排序')
+    icon_url = models.TextField(default='', verbose_name='图标地址')
+    is_show = models.BooleanField(default=True, verbose_name='是否展示')
+    created_time = models.IntegerField(default=0, verbose_name='创建时间')
+    updated_time = models.IntegerField(default=0, verbose_name='更新时间')
+
+    class Meta:
+        db_table = 'feed_type'
+        verbose_name = '喂养类型表'
+        verbose_name_plural = verbose_name
+        app_label = 'Roomumy'
+
+
+class FeedDiary(models.Model):
+    id = models.AutoField(primary_key=True, verbose_name='自增id')
+    baby_id = models.IntegerField(default=0, verbose_name='关联宝宝id')
+    feed_type = models.ForeignKey(FeedType, to_field='id', on_delete=models.CASCADE, default='', verbose_name='喂养类型')
+    # 1:母乳亲喂 2:母乳瓶喂 3:奶粉 4:辅食 5:换尿布 6:身高体重 7:户外活动 8:育儿日记 9:自定义
+    feed_content = models.JSONField(null=True, verbose_name='事件内容')
+    # 当类型为5时,sub_event_type为 1:嘘嘘 2:臭臭 3:嘘嘘+臭臭 4:干爽
+    date_time = models.IntegerField(default=0, verbose_name='事件时间')
+    created_time = models.IntegerField(default=0, verbose_name='创建时间')
+    updated_time = models.IntegerField(default=0, verbose_name='更新时间')
+
+    class Meta:
+        db_table = 'feed_diary'
+        verbose_name = '喂养日记表'
+        verbose_name_plural = verbose_name
+        app_label = 'Roomumy'
+
+
+class AlbumTitle(models.Model):
+    id = models.AutoField(primary_key=True)
+    album_title = models.TextField(blank=True, default='', verbose_name='相册标题')
+    lang = models.CharField(default='', max_length=10, verbose_name='语言')
+    created_time = models.IntegerField(default=0, verbose_name='创建时间')
+    updated_time = models.IntegerField(default=0, verbose_name='更新时间')
+
+    class Meta:
+        db_table = 'album_title'
+        verbose_name = '时光相册默认标题表'
+        verbose_name_plural = verbose_name
+        app_label = 'Roomumy'
+
+
+class TimeAlbum(models.Model):
+    id = models.AutoField(primary_key=True)
+    device_id = models.CharField(default='', max_length=32, verbose_name='关联设备',  db_index=True)
+    album_date = models.IntegerField(default=0, verbose_name='相册日期', db_index=True)
+    album_title = models.TextField(default=0, verbose_name='相册标题')
+    created_time = models.IntegerField(default=0, verbose_name='创建时间')
+    updated_time = models.IntegerField(default=0, verbose_name='更新时间')
+
+    class Meta:
+        db_table = 'time_album'
+        verbose_name = '设备时光相册表'
+        verbose_name_plural = verbose_name
+        app_label = 'Roomumy'
+
+
+class AlbumMedia(models.Model):
+    id = models.AutoField(primary_key=True)
+    time_album_id = models.IntegerField(default=0, verbose_name=u'时光相册表id', db_index=True)
+    # 这三个字段组成存储桶的访问链接
+    device_id = models.CharField(default='', max_length=32, verbose_name='关联设备', db_index=True)
+    channel = models.IntegerField(default=1, verbose_name='通道')
+    event_time = models.IntegerField(default=0, verbose_name='事件时间')
+    # 0表示隐藏 1表示显示 2表示被添加为日记
+    status = models.SmallIntegerField(default=1, verbose_name='状态')
+    # 1: 阿里云, 2: AWS, 3: oci美国凤凰城, 4: oci英国伦敦, 5: 华为云
+    storage_location = models.SmallIntegerField(default=0, verbose_name='存储位置')
+    created_time = models.IntegerField(default=0, verbose_name='创建时间')
+    updated_time = models.IntegerField(default=0, verbose_name='更新时间')
+
+    class Meta:
+        db_table = "album_media"
+        verbose_name = "相册媒体表"
+        verbose_name_plural = verbose_name
+        app_label = 'Roomumy'
+
+
+class TimeDiary(models.Model):
+    id = models.AutoField(primary_key=True)
+    user_id = models.CharField(default='', max_length=32, verbose_name='关联用户id', db_index=True)
+    diary_date = models.IntegerField(default=0, verbose_name='日记日期', db_index=True)
+    created_time = models.IntegerField(default=0, verbose_name='创建时间')
+    updated_time = models.IntegerField(default=0, verbose_name='更新时间')
+
+    class Meta:
+        db_table = "time_diary"
+        verbose_name = "用户时光日记表"
+        verbose_name_plural = verbose_name
+        app_label = 'Roomumy'
+
+
+class DiaryAlbumMediaRelation(models.Model):
+    id = models.AutoField(primary_key=True)
+    diary = models.ForeignKey(TimeDiary, on_delete=models.CASCADE, db_index=True, db_constraint=False, verbose_name="日记")
+    album_media = models.ForeignKey(AlbumMedia, on_delete=models.CASCADE, db_index=True, db_constraint=False, verbose_name="相册媒体")
+    created_time = models.IntegerField(default=0, verbose_name='创建时间')
+    updated_time = models.IntegerField(default=0, verbose_name='更新时间')
+
+    class Meta:
+        db_table = "diary_album_media_relation"
+        verbose_name = "日记与相册媒体关联表"
+        verbose_name_plural = verbose_name
+        app_label = 'Roomumy'
+        unique_together = ('diary', 'album_media')  # 防止重复关联
+
+
+class BabyGrowthStandard(models.Model):
+    age_min = models.IntegerField(verbose_name="最小年龄(天)", help_text="单位:天")
+    age_max = models.IntegerField(verbose_name="最大年龄(天)", help_text="单位:天")
+    gender = models.IntegerField(default=0, verbose_name="性别")
+    height_min = models.FloatField(verbose_name="最小身高 (cm)")
+    height_max = models.FloatField(verbose_name="最大身高 (cm)")
+    weight_min = models.FloatField(verbose_name="最小体重 (kg)")
+    weight_max = models.FloatField(verbose_name="最大体重 (kg)")
+    head_circumference_min = models.FloatField(verbose_name="最小头围 (cm)")
+    head_circumference_max = models.FloatField(verbose_name="最大头围 (cm)")
+    created_time = models.IntegerField(default=0, verbose_name='创建时间')
+    updated_time = models.IntegerField(default=0, verbose_name='更新时间')
+
+    class Meta:
+        db_table = "baby_growth_standard"
+        verbose_name = "婴儿成长标准表"
+        verbose_name_plural = verbose_name
+        app_label = 'Roomumy'
+
+
+class DevelopmentalPoints(models.Model):
+    id = models.AutoField(primary_key=True, verbose_name='自增id')
+    month = models.CharField(default='', max_length=32, verbose_name='月龄')
+    title = models.CharField(default='', max_length=32, verbose_name='标题')
+    weight = models.CharField(default='', max_length=32, verbose_name='体重')
+    height = models.CharField(default='', max_length=32, verbose_name='身高')
+    head_circumference = models.CharField(default='', max_length=32, verbose_name='头围')
+    content = models.TextField(default='', verbose_name='内容')
+
+    class Meta:
+        db_table = "developmental_points"
+        verbose_name = "发育要点"
+        verbose_name_plural = verbose_name
+        app_label = 'Roomumy'

+ 18 - 0
Roomumy/server_urls/roomumy_url.py

@@ -0,0 +1,18 @@
+# -*- encoding: utf-8 -*-
+"""
+@File    : roomumy_url.py
+@Time    : 2024-10-7 14:37:30
+@Author  : peng
+@Email   :
+@Software: PyCharm
+"""
+from django.urls import re_path
+
+from Roomumy.Controller import BabyController, FeedDiaryController, TimeAlbumController, RTCController
+
+urlpatterns = [
+    re_path(r'^baby/(?P<operation>.*)$', BabyController.BabyView.as_view()),
+    re_path(r'^feedDiary/(?P<operation>.*)$', FeedDiaryController.FeedDiaryView.as_view()),
+    re_path(r'^timeAlbum/(?P<operation>.*)$', TimeAlbumController.TimeAlbumView.as_view()),
+    re_path(r'^RTC/(?P<operation>.*)$', RTCController.RTCView.as_view()),
+]

+ 3 - 0
Roomumy/tests.py

@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.

+ 3 - 0
Roomumy/views.py

@@ -0,0 +1,3 @@
+from django.shortcuts import render
+
+# Create your views here.