日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > windows >内容正文

windows

基于 FastAPI 的房源租赁系统设计与实现

發布時間:2023/12/31 windows 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 基于 FastAPI 的房源租赁系统设计与实现 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

項目背景

傳統的線下租房不便、途徑少、信息更新慢,導致房屋租賃效率低。為了有效的提升租賃效率和房源信息管理、提供更優質的租賃服務。讓房東出租宣傳展示與房源管理、租客更好的檢索房源信息、發布租房需求以及入住預定、后臺房源管理、審核等一站式租賃服務平臺。

  • 租客:瀏覽房源、收藏房源、預定房源、發布租房需求、查看電子合同。
  • 房東:發布房源、訂單管理、查看電子合同。
  • 管家:查看房源信息、回復咨詢、線下帶看房源。
  • 管理員:用戶管理、房源管理、訂單管理、租房需求、實名認證、系統公告管理。

TODO

  • 房源全文檢索
  • 租房需求支持評論
  • 日租、合租模式
  • 房源推薦系統(Go開發)

項目特色

  • 采用了七牛云OSS、CDN服務加速一些圖片資源。
  • 采用 FastAPI 的后臺任務實現異步發送短信驗證碼。
  • 采用 tortoise-orm 完成數據庫操作的封裝。
  • 通過模板字符串動態渲染富文本實現電子合同功能。
  • 對接阿里支付實現了訂單、支付模塊,對接百度地圖實現當前城市定位、房源附近信息查詢等功能。
  • 前端界面采用 Vue.js + Element ui 實現數據渲染,Bootstrap 實現自適應布局。

項目體驗

項目體驗地址 http://43.138.220.206:9999/huihome 由于注冊需要發送短信驗證碼,而手機驗證碼服務現在只能給我的測試手機號發送驗證碼,因此不能使用注冊服務。大家可以使用已有賬號去登錄體驗。

賬號類別用戶名密碼備注
用戶賬號hui123456租客賬號
用戶賬號wang123456房東賬號

項目源碼:HuiDBK/HuiHome: 基于FastAPI的房屋租賃系統 (github.com)

項目還沒有太完善,服務器也只是學習級別的,可能會出現很多異常,望大家多擔待。

項目啟動

  • 準備好MySQL 與 Redis數據庫服務,修改相關數據庫配置信息
  • 申請第三方服務:七牛云的OSS服務、容聯云的短信服務、阿里的支付服務、百度地圖服務
  • 依賴于 Python 3.7.9 編程環境
  • 安裝 requirements.txt 項目依賴 pip install -r requirements.txt
  • 啟動項目 python run.py
  • 如果成功在本地啟動項目,訪問 http://127.0.0.1:8080/docs 地址查看接口文檔
  • ?

    項目部署

  • 確保Mysql、Redis服務正常
  • 在存在Dockerfile文件的項目目錄下構建鏡像 docker build -t house_rental_image . (最后.不要忘記)
  • 運行鏡像產生容器 docker run -d --name house_rental_container -p 80:80 house_rental_image
  • docker ps 查看容器是否啟動
  • 系統整體功能圖

    ?

    項目結構

    項目開發整體采用的是Python的FastAPI框架來搭建系統的接口服務,接口設計遵循?Restful API接口規范。接口前后端交互都采用json格式進行數據交互,項目整體的結構如下:

    ?

    項目Redis緩存設計

    Redis key 規范:

    project : module : business : unique key項目名 : 模塊名 : 業務 : 唯一區別key 例如:用戶手機短信驗證碼緩存 house_rental:user:sms_code:13022331752 復制代碼

    用戶模塊緩存

    房源模塊緩存

    ?其他緩存

    ?系統整體ER圖

    ?房屋屬性太多故在整體ER圖省略

    實際表屬性更多進行了垂直分表。

    代碼細節

    實名認證裝飾器

    def real_auth_required(func):""" 實名認證裝飾器 """@wraps(func)async def warp(*args, **kwargs):"""通過請求上下文的user對象來判斷用戶有沒有實名認證"""cur_request = context_util.REQUEST_CONTEXT.get()user = cur_request.user or Noneif not user:raise AuthorizationException()if user.role == UserRole.admin.value:# 管理員不需要實名認證return await func(*args, **kwargs)# 此時不同直接通過 user.auth_status 來驗證# 應該通過 user_id 去數據庫中查詢最新的狀態user_profile = await UserProfileManager.get_by_id(user.id)if user_profile.auth_status != UserAuthStatus.authorized.value:raise BusinessException().exc_data(ErrorCodeEnum.REALNAME_AUTH_ERR)return await func(*args, **kwargs)return warp

    分頁數據封裝裝飾器

    from pydantic import BaseModel, Fieldclass ResponseBaseModel(BaseModel):""" 統一響應模型 """code: intmessage: strdata: dictclass ListResponseDataModel(BaseModel):""" 分頁列表響應data模型 """total: int = Field(default=0, description="數據總數量")data_list: list = Field(default=[], description='數據列表')has_more: bool = Field(default=False, description="是否有下一頁")next_offset: int = Field(default=0, description="offset下次起步")def list_page(func):""" 分頁數據封裝裝飾器 """@wraps(func)async def warp(*args, **kwargs):"""尋找函數參數 ListPageRequestModel 的實例 有獲取 limit、offset所有分頁請求入參都繼承 ListPageRequestModel"""limit, offset = None, None# 位置參數中尋找for arg in args:if limit is not None and offset is not None:breakif isinstance(arg, ListPageRequestModel):limit, offset = arg.limit, arg.offset# 關鍵字參數中尋找for key, value in kwargs.items():if limit is not None and offset is not None:breakif isinstance(value, ListPageRequestModel):# 關鍵字參數值是否是 ListPageRequestModellimit, offset = value.limit, value.offsetelif key == 'limit':# 也支持關鍵參數 key 為 limit 和 offset的情況limit = valueelif key == 'offset':offset = valueif limit is None or offset is None:# 沒有成功賦值, 則不支持logger.debug('不支持分頁數據封裝')# 執行函數獲取分頁響應的數據, 有兩種情況# 1 返回使用了pydantic model ListResponseDataModel (盡量使用這種來返回業務數據)# 2 返回 total data_list (元組)data_obj = await func(*args, **kwargs)# 分頁數據返回的參數都必須遵守 ListResponseDataModelif isinstance(data_obj, ListResponseDataModel):# ListResponseDataModel 處理data_obj.next_offset = offset + limitdata_obj.has_more = False if data_obj.next_offset > data_obj.total else Trueelif isinstance(data_obj, tuple):# 元組 處理total = data_obj[0] if isinstance(data_obj[0], int) else data_obj[1]data_list = data_obj[1] if isinstance(data_obj[1], list) else data_obj[0]data_obj = ListResponseDataModel(total=total,data_list=data_list,next_offset=offset + limit,has_more=False if offset + limit > total else True)list_page_resp = data_objreturn list_page_respreturn warp

    json數據緩存裝飾器

    def cache_json(cache_info=None, key=None, timeout=60):"""緩存裝飾器 (適合緩存字符串json數據):param key: 緩存的key:param timeout: 緩存的時間 默認60秒:param cache_info: 封裝好的緩存信息對象 RedisCacheInfo:return:"""if cache_info:# 有封裝的緩存對象key = cache_info.keytimeout = cache_info.timeoutdef cache_decorator(api_func):@wraps(api_func)async def warp(*args, **kwargs):# 1、沒有設置key則根據接口函數的信息和系統密鑰自動生成(盡量設置key)nonlocal keyif not key:# 應用名:函數所在模塊:函數名:函數位置參數:函數關鍵字參數:系統密鑰 進行hashparam_args_str = ','.join([str(arg) for arg in args])param_kwargs_str = ','.join(sorted([f'{k}:{v}' for k, v in kwargs.items()]))hash_str = f'{constants.APP_NAME}:{api_func.__module__}:{api_func.__name__}:' \f'{param_args_str}:{param_kwargs_str}:{settings.SECRET}'has_result = hashlib.md5(hash_str.encode()).hexdigest()# 根據哈希結果生成keykey = f'{constants.APP_NAME}:{api_func.__module__}:{api_func.__name__}:{has_result}'# 2、先查看是否有緩存from house_rental.commons.utils.redis_util import RedisUtilredis_client = await RedisUtil().get_redis_conn()cache_data = await redis_client.get(key)if cache_data:return json.loads(cache_data)# 3、執行接口函數獲取結果api_result = await api_func(*args, **kwargs)# 4、設置緩存if isinstance(api_result, BaseModel):# 結果是pydantic的模型對象處理api_result_json = api_result.json()elif isinstance(api_result, dict):# 字典api_result_json = json.dumps(api_result)else:# 其他可以json序列化的api_result_json = json.dumps(api_result)await redis_client.setex(key, timeout, api_result_json)return api_resultreturn warpreturn cache_decorator

    項目接口依賴(Depends)

    async def jwt_authentication(request: Request):""" jwt 鑒權"""# for api_url in settings.API_URL_WHITE_LIST:# # 在白名單的接口無需token驗證# if str(request.url.path).startswith(api_url):# returntoken = request.headers.get('Authorization') or Noneif not token:raise AuthorizationException()# Bearer 占了7位if not str(token).startswith('Bearer '):raise AuthorizationException()token = str(token)[7:]user_info = jwt_util.verify_jwt(token)if not user_info:# 無效tokenraise AuthorizationException()# 校驗通過保存到request.user中user_id = user_info.get('user_id')user = await UserBasicManager.get_by_id(user_id)if user.role != UserRole.admin.value and str(request.url.path).startswith('/api/v1/admin'):# 不是管理員無法訪問了后臺模塊接口raise AuthorizationException()request.scope['user'] = userasync def request_context(request: Request):""" 保存當前request對象到上下文中 """context_util.REQUEST_CONTEXT.set(request)async def login_required(request: Request):""" 登錄權限校驗 """try:user = request.userexcept:raise AuthorizationException().exc_data(ErrorCodeEnum.AUTHORIZATION_ERR)if not user:raise AuthorizationException().exc_data(ErrorCodeEnum.AUTHORIZATION_ERR)

    響應序列化遞歸工具函數

    def obj2DataModel(data_obj: Union[Dict,Type[BaseOrmModel],List[Dict],List[BaseOrmModel]],data_model: Type[BaseModel] ) -> Union[BaseModel, List[BaseModel], None]:"""將數據對象轉換成 pydantic的響應模型對象, 如果是數據庫模型對象則調用to_dict()后遞歸:param data_obj: 支持 字典對象, 數據庫模型對象, 列表對象:param data_model: 轉換后數據模型:return:"""if isinstance(data_obj, dict):# 字典處理return data_model(**data_obj)elif isinstance(data_obj, BaseOrmModel):# 數據模型對象處理, to_dict()后遞歸調用return obj2DataModel(data_obj.to_dict(), data_model=data_model)elif isinstance(data_obj, list):# 列表處理return [obj2DataModel(item, data_model=data_model) for item in data_obj]else:logger.debug(f'不支持此{data_obj}類型的轉換')return

    入參、出參代碼樣式

    未調整前:

    ?調整后:

    ?

    ?

    不要為了單獨一個而全部調整,只要達到了美觀就行無需全部對齊,重要是關注每個入參和出參有沒有關聯等,一些必傳啊,枚舉參數等,相關聯的放到一起,這樣的代碼才更賞心悅目。

    房源設施雙色圖標展示

    首先我可以獲取所有房源設施的信息,接口返回當前房源有的房源信息,只要判斷不在總房源設施列表里的就顯示 灰色圖標、文字下劃線 在則顯示不同的顏色(數據庫只存了灰色圖標)

    1、通過 filter 函數濾鏡函數實現圖標不同顏色的陰影,然后原圖標偏移圖標寬度然后隱藏,就只剩下帶顏色的圖標陰影(本項目所采用的方案)

    .facility_no {filter: grayscale(100%);-webkit-filter: grayscale(100%);-moz-filter: grayscale(100%);-o-filter: grayscale(100%);text-decoration: line-through; }.facility_yes {filter: drop-shadow(46px 0px 0px #fd5332);backdrop-filter: blur(0px); }.facility_text {width: 46px;text-align: center; }.facility_hidden {width: 46px;height: 46px;text-indent: -46px;overflow: hidden; }<li v-for="item in all_house_facility"><div class="facility_no"v-if="house_facility_ids.indexOf(item.facility_id) == -1"><div><img :src="item.icon" :title="item.name" width="46" :alt="item.name"height="46"></div><p class="facility_text">{{ item.name }}</p></div><div v-else><div class="facility_hidden"><img :src="item.icon" :title="item.name" class="facility_yes"width="46"height="46"></div><p class="facility_text">{{ item.name }}</p></div> </li>

    2、數據庫存存儲兩張不同顏色的圖標

    3、數據庫還是存儲一張圖標但一張圖標包含兩種圖標,前端通過切圖來分割圖標?background-image?屬性搭配

    background-positon:x軸起點 y軸起點; background-size:背景圖片的大小; width:終點x軸位置; height:終點y軸位置;

    支付狀態章印顯示

    通過?position?屬性實現子絕父相定位章印元素,border-radius?控制邊框圓角

    ?

    .seal{width: 115px;height: 115px;border: solid 5px #B4B4B4;border-radius: 100%;background-color: rgba(255, 255, 255, 0.8);position: relative;display: flex;justify-content: center;align-items: center; } .seal-son{width: 110px;height: 110px;border: solid 2px #B4B4B4;border-radius: 100%;background-color: rgba(255, 255, 255, 0.8);position: relative; }.seal-lg-text{position: absolute;top: 32px;text-align: center;font-size: 18px;transform: rotate(-45deg);right: 40px;color: #B4B4B4;font-weight: 900; } .seal-sm-text{position: absolute;top: 66px;text-align: center;font-size: 10px;transform: rotate(-45deg);left: 40px;color: #B4B4B4; }

    實際使用如果位置不夠好,通過style重寫覆蓋css屬性調整,有點像類繼承一樣

    <div class="seal" style="position: absolute;right: -12px;top: 45px;"><div class="seal-son"><span class="seal-lg-text"><span v-if="order_detail_item.state === order_state_enum.payed">已支付</span><span v-else-if="order_detail_item.state === order_state_enum.ordered">已預訂</span><span v-else-if="order_detail_item.state === order_state_enum.no_pay">未支付</span><span v-else-if="order_detail_item.state === order_state_enum.finished">已完成</span><span v-else-if="order_detail_item.state === order_state_enum.canceled">已取消</span></span><span class="seal-sm-text">{{ order_detail_item.update_ts }}</span></div> </div>

    元素大小縮放提高交互

    .el_scale:hover {transform: scale(1.03) }

    項目界面展示

    首頁

    ?

    ?登錄注冊

    ?房源列表

    ?房源收藏

    房源詳情

    ??

    房源地圖服務

    房源訂單

    電子合同

    求租管理

    系統公告

    房東房源管理

    房東發布房源

    總結

    以上是生活随笔為你收集整理的基于 FastAPI 的房源租赁系统设计与实现的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。