网站建设行业 前景,网站建设与维护招聘条件,网站建设技术外包,网络seo是什么0. 开发说明 
在学习开发本项目之前#xff0c;必须保证有以下知识储备和环境工具。 
技术栈说明python3.9、pydantic2.7.1python基础#xff0c;http协议fastapi0.111.0web协程异步框架#xff0c;有web开发基础#xff0c;异步编程#xff0c;类型标注[pyth…0. 开发说明 
在学习开发本项目之前必须保证有以下知识储备和环境工具。 
技术栈说明python3.9、pydantic2.7.1python基础http协议fastapi0.111.0web协程异步框架有web开发基础异步编程类型标注[python3.6提供的typing模块]mysql8.0、Tortoise-ORM0.20.1mysql数据库相关redis 6.xredis数据库相关微信开发者工具、uni-app、HbuilderX编辑器开发小程序项目的UI框架有小程序开发基础vue3.x、vite前端web开发框架git代码版本管理工具docker、docker-compose镜像与容器基本操作 
1. 项目构建 
1.1 服务端构建 
手动创建工程目录路径不要使用中文或者特殊符号。 
fastchat创建虚拟环境终端下执行命令如下 
conda create -n fastchat python3.10安装完成以后需要激活当前虚拟环境[切换python解释器]终端执行如下命令 
conda activate fastchat1.1.1 依赖安装 
pip install -U python-dotenv -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install -U fastapi -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install -U uvicorn[standard] -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install -U tortoise-orm[aiomysql] -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install -U cryptography -i https://pypi.tuna.tsinghua.edu.cn/simple1.1.2 项目目录 
工程目录尽量和虚拟环境的名称保持一致pycharm一般都可以自动识别。如果pycharm不能自动识别则点击编辑器右下角选择自定义解释器即可。 
fastchat/ # 工程目录
├─api/    # api服务端-基于fastAPI框架
│  ├─application/   # 项目代码存储目录
│  │  └─__init__.py  # 项目初始化文件【创建App应用对象的函数各种模块初始化】
│  └─main.py # 服务端程序入口api/main.py代码 
import os
import uvicorn
from application import create_app, FastAPIapp: FastAPI  create_app()app.get(/api)
async def api() - dict:测试接口:return:return {title: api测试接口}if __name__  __main__:uvicorn.run(main:app,host0.0.0.0,port8000,reloadTrue)api/application/__init__.py代码 
from fastapi import FastAPIdef create_app() - FastAPI:创建web应用对象app: FastAPI  FastAPI()return app 
通过api/main.py启动api服务端项目访问地址http://127.0.0.1:8000/api效果如下 1.1.3 项目配置 
在原目录结构基础上增加settings.py与.env、.gitignore效果如下 
fastchat/ # 工程目录
├─api/    # api服务端-基于fastAPI框架
│  ├─application # 项目代码存储目录
│  │  ├─settings.py # 项目配置文件
│  │  └─__init__.py  # 项目初始化文件【创建App应用对象的函数各种模块初始化】
│  ├─.env       # 环境配置文件[被settings.py加载]不会被git记录
│  └─main.py # 服务端程序入口
└─.gitignore   # git忽略文件配置api/.env代码 
# -------- 常规配置 --------
APP_ENVdev        # 当前开发环境
APP_NAMEfastchat  # 应用默认名称
APP_PORT8000      # web服务器监听端口
APP_HOST0.0.0.0   # web服务器监听地址0.0.0.0表示监听任意指向当前服务器的地址
APP_VERSIONv0.0.1 # 项目版本
APP_DEBUGtrue     # 调试模式true表示开启
APP_TIMEZONEAsia/Shanghai  # 时区.gitignore代码 
.env
.idea
__pycache__api/application/__init__.py代码 
from fastapi import FastAPI
from dotenv import load_dotenvdef create_app() - FastAPI:创建web应用对象app: FastAPI  FastAPI()# 加载.env文件中的环境变量load_dotenv()return appapi/main.py代码 
import os
import uvicorn
from application import create_app, FastAPIapp: FastAPI  create_app()app.get(/api)
async def api() - dict:测试接口:return:return {title: f{os.environ.get(APP_NAME)}测试接口}if __name__  __main__:uvicorn.run(main:app,hostos.environ.get(APP_HOST),portint(os.environ.get(APP_PORT)),reloadTrue)重启项目如果项目运行正常并刷新浏览器后效果如下则表示配置正确 1.1.3.1 数据库配置 
在终端下创建数据库执行命令如下 
# 先进入数据库交互终端
mysql -uroot -p
# 执行数据库创建语句
create database fastchat;
# 创建管理账户格式CREATE USER 用户名允许账号连接的主机地址 IDENTIFIED BY 密码;
CREATE USER fastchat% IDENTIFIED BY fastchat;
# 给新账户分配管理数据库的权限格式GRANT 管理权限 ON 数据库名.数据表名 TO 用户名允许账号连接的主机地址;
GRANT ALL PRIVILEGES ON fastchat.* TO fastchat%;执行效果如下 api/.env环境配置中新增配置项代码如下 
# -------- 数据库配置 --------
DB_HOST127.0.0.1    # 数据库地址
DB_PORT3306           # 数据库端口
DB_USERfastchat      # 用户名
DB_PASSWORDfastchat   # 密码
DB_DATABASEfastchat     # 数据库名
DB_CHARSETutf8mb4      # 连接编码
DB_POOL_MINSIZE10      # 连接池中的最小连接数
DB_POOL_MAXSIZE30     # 连接池中的最大连接数手动创建配置文件api/application/settings.py编写tortoise-orm的配置信息代码 
import os
tortoise-orm数据库配置
TORTOISE_ORM  {connections: {default: {engine: tortoise.backends.mysql,  # MySQL or Mariadbcredentials: {  # 连接参数host: os.environ.get(DB_HOST, 127.0.0.1),  # 数据库IP/域名地址port: int(os.environ.get(DB_PORT, 3306)),  # 端口user: os.environ.get(DB_USER, root),  # 连接账户password: os.environ.get(DB_PASSWORD, 123),  # 连接密码database: os.environ.get(DB_DATABASE, fastchat),  # 数据库charset: os.environ.get(DB_CHARSET, utf8mb4),  # 编码minsize: int(os.environ.get(DB_POOL_MINSIZE, 1)),  # 连接池中的最小连接数maxsize: int(os.environ.get(DB_POOL_MAXSIZE, 5)),  # 连接池中的最大连接数echo: bool(os.environ.get(DEBUG, True))  # 执行数据库操作时是否打印SQL语句}}},apps: {  # 默认所在的应用目录models: {  # 数据模型的分组名models: [],  # 模型所在目录文件的导包路径[字符串格式]default_connection: default,  # 上一行配置中的模型列表的默认连接配置}},# 时区设置# 当use_tzTrue当前tortoise-orm会默认使用当前程序所在操作系统的时区# 当use_tzFalse时当前tortoise-orm会默认使用timezone配置项中的时区use_tz: False,timezone: os.environ.get(APP_TIMEZONE, Asia/Shanghai)
} 
注册Tortoise-ORM到FastAPI应用对象中。api/application/__init__.py代码 
from fastapi import FastAPI
from dotenv import load_dotenv
from tortoise.contrib.fastapi import register_tortoise
from . import settingsdef create_app() - FastAPI:创建web应用对象app: FastAPI  FastAPI()# 加载.env文件中的环境变量load_dotenv()# 把Tortoise-orm注册到App应用对象中register_tortoise(app,configsettings.TORTOISE_ORM,generate_schemasFalse,  # 是否自动生成表结构add_exception_handlersTrue,  # 是否启用自动异常处理)return app 
完成上面的配置以后因为tortoise-orm默认并没有连接数据库因此我们需要编写一个数据表模型进行数据库连接操作以测试连接配置是否正确不过这块我们先放一放因为项目开发过程中有可能数据库需要保存很多数据自然也就需要创建对应很多的数据表模型而不同的数据对应的功能业务是不同的因此我们需要分开写在不同的文件或者目录下所以我们得先配置应用分组不同的功能分属于不同的应用下每一个应用都属于自己的数据表模型、api视图接口、路由数据等。 
1.1.3.2 应用分组 
首先创建分组应用存储目录apps并在apps目录下先创建2个应用分组目录分别是common公共数据应用分组与users用户数据应用分组目录结构如下 
fastchat/ # 工程目录
├─api/    # api服务端-基于fastAPI框架
│  ├─application # 项目代码存储目录
│  │  ├─apps/     # 分组应用存储目录
│  │  │  ├─__init__.py
│  │  │  ├─common/        # 公共数据的应用分组
│  │  │  │  ├─models.py   # 表模型文件
│  │  │  │  ├─views.py      # api视图接口文件
│  │  │  │  └─scheams.py # 请求与响应数据模型文件
│  │  │  └─users/              # 用户数据的应用分组
│  │  │      ├─models.py   # 表模型文件
│  │  │      ├─views.py      # api视图接口文件
│  │  │      └─scheams.py # 请求与响应数据模型文件
│  │  ├─settings.py # 项目配置文件
│  │  └─__init__.py  # 项目初始化文件【创建App应用对象的函数各种模块初始化】
│  ├─.env       # 环境配置[被settings.py加载]不会被git记录
│  └─main.py # 服务端程序入口
└─.gitignore   # git忽略文件配置接下来我们可以把最初编写在入口程序的测试api视图接口转移到common应用分组目录下的views.py接口视图文件中api/application/apps/common/views.py代码 
import os
from fastapi import APIRouterapp  APIRouter()app.get(/api)
async def api() - dict:测试接口:return:return {title: f{os.environ.get(APP_NAME)}测试接口} 
入口程序文件api/main.py中不再编写api视图接口代码 
import os
import uvicorn
from application import create_app, FastAPIapp: FastAPI  create_app()# app.get(/api)
# async def api() - dict:
#     
#     测试接口
#     :return:
#     
#     return {title: f{os.environ.get(APP_NAME)}测试接口}if __name__  __main__:uvicorn.run(main:app,hostos.environ.get(APP_HOST),portint(os.environ.get(APP_PORT)),reloadTrue)因为common应用分组是我们自定义的目录所以FastAPI默认是不识别的所以需要手动把应用分组下的路由注册到App应用对象中api/application/__init__.py代码 
from fastapi import FastAPI
from dotenv import load_dotenv
from tortoise.contrib.fastapi import register_tortoise
from . import settings
from .apps.common.views import app as common_appdef create_app() - FastAPI:创建web应用对象app: FastAPI  FastAPI()# 加载.env文件中的环境变量load_dotenv()# 把Tortoise-orm注册到App应用对象中register_tortoise(app,configsettings.TORTOISE_ORM,generate_schemasFalse,  # 是否自动生成表结构add_exception_handlersTrue,  # 是否启用自动异常处理)# 注册各个应用分组下的路由信息合并到App应用对象app.include_router(common_app, prefix)return app 
再次重启api服务端项目访问http://127.0.0.1:8000/api输出内容依旧则表示配置成功。 
接下来我们就可以在users分组应用中创建属于用户相关的数据表模型了api/application/apps/users/models.py代码 
from tortoise import models, fieldsclass User(models.Model):# 字段列表id  fields.IntField(pkTrue, description主键)username  fields.CharField(max_length255, uniqueTrue, description账号)nickname  fields.CharField(max_length255, indexTrue, description昵称)password  fields.CharField(max_length255, description密码)openid    fields.CharField(max_length255, uniqueTrue, descriptionOpenID)mobile    fields.CharField(max_length15, indexTrue, description手机)avatar    fields.CharField(max_length500, nullTrue, description头像)country    fields.CharField(max_length255, nullTrue, description国家)province    fields.CharField(max_length255, nullTrue, description省份)city    fields.CharField(max_length255, nullTrue, description城市)sex  fields.BooleanField(defaultTrue, nullTrue, description性别)created_time  fields.DatetimeField(auto_now_addTrue, description创建时间)updated_time  fields.DatetimeField(auto_nowTrue, description更新时间)deleted_time  fields.DatetimeField(nullTrue, description删除时间)# 元数据class Meta:table  user_infodescription  用户信息def __repr__(self):return fUser (id{self.id}, username{self.username})__str__  __repr__ 
完成模型创建以后接下来只需要在api/application/settings.py中把当前新增模型的路径添加到models配置项中代码 
import os
tortoise-orm数据库配置
DEBUG  os.environ.get(DEBUG, True)TORTOISE_ORM  {connections: {.....},apps: {  # 默认所在的应用目录models: {  # 数据模型的分组名models: [application.apps.users.models],  # 模型所在目录文件的导包路径[字符串格式]从main.py所在路径开始编写default_connection: default,  # 上一行配置中的模型列表的默认连接配置}},.....
} 
注册模型到tortoise-orm中以后在api/application/__init__.py初始化文件中把generate_schemas的值改为True让tortoise-orm自动根据模型建表。api/application/__init__.py代码如下 
from fastapi import FastAPI
from dotenv import load_dotenv
from tortoise.contrib.fastapi import register_tortoise
from . import settings
from .apps.common.views import app as common_appdef create_app() - FastAPI:创建web应用对象app: FastAPI  FastAPI()# 加载.env文件中的环境变量load_dotenv()# 把Tortoise-orm注册到App应用对象中register_tortoise(app,configsettings.TORTOISE_ORM,generate_schemasTrue,  # 是否自动生成表结构add_exception_handlersTrue,  # 是否启用自动异常处理)# 注册各个应用分组下的路由信息合并到App应用对象app.include_router(common_app, prefix)return app 
OK重启项目查看终端如果正常启动则表示上面的所有操作正确。继续登陆MySQL数据库查看是否建表成功效果如下 建表成功表示tortoise-orm配置正确接下来我们可以考虑使用数据迁移来管理数据表模型与MySQL数据表的修改记录对应关系。所以先鼠标右键删除数据库中新建的user_info数据表并在api/application/__init__.py初始化文件中把generate_schemas的值改为False操作与代码如下 1.1.3.3 数据迁移 
对Tortoise-ORM使用数据迁移根据模型创建数据表会更加友好更加方便安装aerich数据迁移工具执行命令如下 
pip install -U aerich -i https://pypi.tuna.tsinghua.edu.cn/simple把aerich注册到Tortoise-ORM中api/application/settings.py代码 
# TORTOISE ORM的数据库连接配置
TORTOISE_ORM  {....apps: {  # 默认所在的应用目录models: {  # 数据模型的分组名models: [application.apps.users.models, aerich.models],  # 模型所在目录文件的导包路径[字符串格式]default_connection: default,  # 上一行配置中的模型列表的默认连接配置}},...
}使用aerich进行迁移初始化打开终端执行命令如下 
cd api/
aerich init -t application.settings.TORTOISE_ORM生成数据迁移文件终端命令执行如下 
aerich init-db操作效果如下 1.1.3.4 日志配置 
api/application/utils/log.py代码 
import logging
from logging import handlers, Loggerdef getLogger(name: strroot) - Logger:获取日志器对象:param name: 日期器名字默认为root:return: 日志器对象# 1、创建一个logger日期器对象logger: Logger  logging.getLogger(name)# 2、设置下logger的日志的等级logger.setLevel(logging.DEBUG)if not logger.handlers:# 3、创建合适的Handler(FileHandler要有保存路径)th: logging.StreamHandler  logging.StreamHandler()  # 终端处理器rf: handlers.RotatingFileHandler  handlers.RotatingFileHandler(  # 按文件大小分割日志filenameflog/{name}.log, # 日志文件名日志目录log需要手动创建modea,  # aappend 追加写入maxBytes300*1024*1024,  # 单个日志文件大小的最大值backupCount10,  # 备份日志文件的数量所有日志数量  backupCountfilenameencodingutf-8 # 日志文件内容的编码)# 4、设置下每个Handler的日志等级【Handler的日志等级会覆盖上面logger的日志的等级】th.setLevel(logging.DEBUG)rf.setLevel(logging.INFO)# 5、创建下日志的格式器对象formattersimple_formatter: logging.Formatter  logging.Formatter(fmt{levelname} {asctime} {pathname}:{lineno} {message},style{)verbose_formatter: logging.Formatter  logging.Formatter(fmt【{name}】{levelname} {asctime} {pathname}:{lineno} {message},datefmt%Y-%m-%d %H:%M:%S,style{)# 6、向Handler中添加上面创建的格式器对象th.setFormatter(simple_formatter)rf.setFormatter(verbose_formatter)# 7、将上面创建的Handler处理器添加到logger日志器中logger.addHandler(th)logger.addHandler(rf)return loggerif __name__  __main__:# 8. 调用日志器对象logger打印输出日志logger  getLogger(dl)logger.info(这里是常规运行日志)logger.debug(开发人员在调试程序时自己手动打印的日志)logger.warning(这里是程序遇到未来会废弃的函数/方法时输出的警告日志)logger.error(这里是程序发生错误时输出的日志)logger.critical(这是致命级别的日志需要紧急修复的)# 多次调用实例化出来的日志对象如果name相同则得到的是同一个日志器对象单例模式logger1  getLogger(dl)print(id(logger1), id(logger))api/application/utils/middleware.py代码 
import os, time
from .log import getLoggerasync def log_requests(request, call_next):日志中间件logger  getLogger(os.environ.get(APP_NAME))start_time  time.time()response  await call_next(request)process_time  (time.time() - start_time) * 1000formatted_process_time  {0:.2f}.format(process_time)logger.info(fpath{request.url.path} timer{formatted_process_time}ms status_code{response.status_code})return response 
注册中间件到App应用对象代码 
from fastapi import FastAPI
from dotenv import load_dotenv
from tortoise.contrib.fastapi import register_tortoise
from . import settings
from .apps.common.views import app as common_app
from .utils import middlewaredef create_app() - FastAPI:创建web应用对象app: FastAPI  FastAPI()# 加载.env文件中的环境变量load_dotenv()# 把Tortoise-orm注册到App应用对象中register_tortoise(app,configsettings.TORTOISE_ORM,generate_schemasFalse,  # 是否自动生成表结构add_exception_handlersTrue,  # 是否启用自动异常处理)# 注册各个应用分组下的路由信息合并到App应用对象app.include_router(common_app, prefix)# 注册中间件函数http_middleware  app.middleware(http)http_middleware(middleware.log_requests)return app1.1.3.5 异常处理 
1.定义四个文件exception.py(全局处理), main.py(主程序文件), user/user.py(业务模块), user/exception.py(用户模块自己的错误处理)2.exception.py文件
# from fastapi.exceptions import HTTPException
from starlette.exceptions import HTTPException  # 官方推荐注册异常处理器时应该注册到来自 Starlette 的 HTTPException
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse# 全局异常       
async def global_exception_handler(request, exc):if exc.status_code  500:err_msg  Server Internal Errorelse:err_msg  exc.detailreturn JSONResponse({code: exc.status_code,err_msg: err_msg,status: Failed})# 请求数据无效时的错误处理example: http://127.0.0.1/user/{user_id}
success: http://127.0.0.1/user/1
failed: http://127.0.0.1/user/dasync def validate_exception_handler(request, exc):err  exc.errors()[0]return JSONResponse({code: 400,err_msg: err[msg],status: Failed})golbal_exception_handlers  {HTTPException: global_exception_handler,RequestValidationError: validate_exception_handler
}class BaseAPIException(HTTPException):status_code  400detail  api errordef __init__(self, detail: str  None, status_code: int  None):self.detail  detail or self.detailself.status_code  status_code or self.status_code3.定义user/exception.py
from exception import BaseAPIExceptionclass UserDoesNotExistsException(BaseAPIException):status_code  10000detail  user does not exists4.定义uers/user.py
from fastapi.routing import APIRouterfrom .exception import UserDoesNotExistsExceptionrouter_user  APIRouter(prefix/user, tags[用户模块])router_user.get(/{user_id})
async def get_id_by_user(user_id: int):if user_id ! 1:# 这里使用我们自定义的用户错误处理# 返回的统一响应格式{code:10000,err_msg:user does not exists,status:Failed}raise UserDoesNotExistsExceptionreturn {user_id: user_id}5.定义main.py
from fastapi import FastAPI
from exception import golbal_exception_handlers
from user.user import router_userapp  FastAPI(debugTrue, exception_handlersgolbal_exception_handlers)app.include_router(router_user, prefix/api/v1)if __name__  __main__:import uvicornuvicorn.run(appmain:app, host0.0.0.0, port9002, reloadTrue)6.响应
# example: http://127.0.0.1:9002/api/v1/user/2
{code:10000,err_msg:user does not exists,status:Failed}
# example: http://127.0.0.1:9002/api/v1/user/d
{code:400,err_msg:value is not a valid integer,status:Failed}1.2 客户端构建 
启动hbuilderX编辑器点击文件→新建N→1.项目 选择uni-app类型输入项目名称选择模板点击创建N即可成功创建uni-app项目。这里我的项目名称uniapp。 1.2.1 运行项目 
使用快捷键CtrlAlt,打开设置窗口→运行设置→小程序运行设置填写微信开发者工具路径。 注意如果没有安装点击蓝色链接去下载安装并在安装完成以后启动微信开发者工具进入设置窗口→安全把服务端端口和自动化接口…信任项目等配置项打开如下 小程序配置中可以直接使用测试AppID也可以使用真实账户的AppID设置→基本设置→账号信息 进入uniapp项目点击工具栏的运行 - 运行到小程序模拟器 - 微信开发者工具 
1.2.2 目录结构 
fastchat/
├─
├─api/    # 服务端-基于fastAPI框架
│  └── main.py # 服务端程序入口 
└─uniapp/├─unpackage          编译结果存储目录 (一般存放运行或发行的编译结果)├─pages/                代码文件存储目录 │   ├─index/│   │  └─index.vue      对话页面│   └─login/│        └─login.vue      登陆页面├─static/                 静态资源如图片、视频等的存储目录├─App.vue               应用配置用来配置App全局样式以及监听、应用生命周期├─index.html           程序入口├─main.js                 Vue初始化入口文件├─manifest.json       配置应用名称、appid、logo、版本等打包信息├─pages.json           配置页面路由、导航条、选项卡等页面类信息└─uni.scss              内置的常用样式变量pages.json 配置页面路径和基本样式效果 
{pages: [ //pages数组中第一项表示应用启动页参考https://uniapp.dcloud.io/collocation/pages{path: pages/index/index,style: {navigationBarTitleText: Chat}},{path : pages/login/login,style : {navigationBarTitleText : login}}],window: {navigationBarBackgroundColor: #ff00ff,navigationBarTextStyle: black,navigationBarTitleText: Chat},globalStyle: {navigationBarTextStyle: black,navigationBarTitleText: Chat,navigationBarBackgroundColor: #F8F8F8,backgroundColor: #F8F8F8},uniIdRouter: {}
}1.2.3 界面效果 
1.2.3.1 登陆页面 
uniapp/pages/login/login.vue代码 
templateview classcontent    view classloginBoxh3 styletext-align: center;margin-bottom:120rpx;欢迎登录/h3view classinputBoxview classiptuni-icons typecontact size24 colorrgb(66,157,250)/uni-iconsinput typetext value placeholder请输入账号//viewview classiptuni-icons typeeye size24 colorrgb(66,157,250)/uni-iconsinput typepasssword value placeholder请输入密码//viewview classiptuni-icons typecheckmarkempty size24 colorrgb(66,157,250)/uni-iconsinput typetext value placeholder请输入验证码/view classyzm验证码/view/viewbutton classlogin-btn登录/button/viewview classtipboxview classtxt —— 其他账号登录 —— /viewview classotherUserbuttonuni-icons typeqq size40 colorrgb(66,157,250)/uni-icons/buttonbutton open-typegetUserInfo getuserinfowxLoginuni-icons typeweixin size40 colorrgb(2,187,17)/uni-icons/button/view/view/view/view
/templatescript setup/scriptstyle scopedsvg {position: absolute;bottom: 0;left: 0;width: 100%;height:40%;box-sizing: border-box;display: block;background-color: #ffffff;}.loginBox{position: absolute;top: 50%;left: 50%;transform: translate(-50%,-60%);width: 90%;border-radius: 20rpx;padding: 60rpx;box-sizing: border-box;}h3{color:rgb(66,157,250);font-size: 40rpx;letter-spacing: 10rpx;margin-bottom: 40rpx;}.inputBox{}.ipt{height: 86rpx;display: flex;justify-content: flex-start;align-items: center;margin-bottom: 40rpx;background-color: #f5f5f5;border-radius: 10rpx;padding-left: 10rpx;}.ipt input{margin-left: 20rpx;font-size: 28rpx;}.ipt input{margin-left: 20rpx;}.forgetPwd{margin-top: 30rpx;font-size: 26rpx;color: #b5b5b5;text-align: end;padding:0 10rpx;display: flex;justify-content: space-between;}.login-btn{margin-top: 20rpx;line-height: 85rpx;text-align: center;background: rgb(66,157,250);border-radius: 40rpx;color: #fff;margin-top: 40rpx;}.tip{text-align: center;font-size: 28rpx;position: fixed;bottom: 50rpx;left: 50%;transform: translate(-50%,-50%);color: #f4f4f4;}.tipbox {text-align: center;margin-top: 100rpx;}.otherUser {margin-top: 30rpx;display: flex;justify-content: center;}.otherUser button{margin: 0 10px;padding: 0;height: 42px;line-height: 42px;background: transparent;border: 1px solid transparent;outline: none;}.txt {font-size: 28rpx;color: #cbcbcb;}.otherUser .uni-icons {margin-left: 20rpx;}.yzm{text-align: end;font-size: 24rpx;background: rgb(66,157,250);height: 60rpx;width: 150rpx;line-height: 60rpx;text-align: center;border-radius: 10rpx;color: #fff;}
/style展示效果如下 1.2.3.2 聊天页面 
uniapp/pages/index/index.vue代码 
template
view classpage-layoutview classpage-body idx_chatview :keyindex v-for(message, index) in messagesview classchat-item-bodyview classchat-item-time{{message.time}}/viewview keyindex v-ifmessage.type  ai classchat-item-layout chat-leftview classchat-inner-layoutview classchat-item-name{{message.sender}}/viewview classchat-item-msg-layoutimage classchat-item-photo v-ifmessage.photoUrl :srcmessage.photoUrl modeaspectFit/imageview classchat-inner-msg-left v-htmlmessage.text/view/view/view/view/viewview :keyindex v-ifmessage.type  sender classchat-item-layout chat-rightview classchat-inner-layoutview classchat-item-name-right{{message.sender}}/viewview classchat-item-msg-layoutview classchat-inner-msg-right v-htmlmessage.text/viewimage classchat-item-photo v-ifmessage.photoUrl :srcmessage.photoUrl modeaspectFit/image/view/view/view/view/viewview classsubmit-layoutinput classsubmit-input placeholder点击输入开始聊天吧 v-modeluserInput/view classsubmit-submit typesubmit sizemini clicksendMessage发送/view/view
/view
/templatescript setup
import {ref} from vue;
const userInput  ref();
const messages  ref([{type: sender,text: 你是谁,time: 2024-05-03 14:13:22,photoUrl: https://pic1.zhimg.com/80/v2-0aca47cf23db7047d051f03297312d64_720w.webp,},{type: ai,text: 我是ChatGPT一个由OpenAI开发的大型语言模型。我基于GPT-4架构构建旨在通过自然语言处理技术帮助用户解决各种问题、回答问题、提供建议和进行对话。brbr我能够理解和生成文本处理从简单问题到复杂任务的广泛请求包括但不限于编写代码、创建内容、提供解释和建议、以及进行翻译。我的知识库截止到2023年10月这意味着我能提供的信息和回答基于我在那之前的训练数据。brbr我不是一个真人而是一个由人工智能驱动的程序旨在通过文本形式与用户进行互动。我的目的是帮助用户找到他们需要的信息解决问题或者提供有价值的对话。,time: 2024-01-26 13:43:15,photoUrl: https://www.lulinux.com/d/file/bigpic/az/234906/xldp0zb1vlw.png,}
])const pageScrollToBottom  (){let that  this;wx.createSelectorQuery().select(#x_chat).boundingClientRect(function (rect) {let top  rect.height * messages.value.length;wx.pageScrollTo({scrollTop: top,duration: 100})}).exec()
}
pageScrollToBottom();const sendMessage  (){if (userInput.value.trim()  ) return;const userMessage  {type: sender,text: userInput.value,time: 2024-01-26 13:59:12,photoUrl: https://pic2.zhimg.com/80/v2-ab37ad93a61fc94135f1c67ea2412c55_720w.webp,};messages.value.push(userMessage);userInput.value  ;pageScrollToBottom();
}/scriptstyle
.page-layout {width: 100%;height: 100%;box-sizing: border-box;
}.page-body {width: 100%;display: flex;flex-direction: column;padding-bottom: 56px;
}.chat-item-body {display: flex;flex-direction: column;margin-top: 20rpx;
}.chat-item-time {width: 100vw;text-align: center;font-size: 28rpx;color: #ccc;border-radius: 10rpx;margin-top: 40rpx;
}.chat-item-layout {display: block;max-width: 82%;margin: 1rpx 5rpx;box-sizing: border-box;padding: 0 1rpx;
}.chat-right {float: right;
}.chat-left {float: left;
}.chat-inner-layout {display: flex;flex-direction: column;
}.chat-item-photo {width: 70rpx;height: 70rpx;min-width: 70rpx;min-height: 70rpx;border-radius: 50%;
}.chat-item-msg-layout {display: flex;flex-direction: row;
}.chat-item-name {display: flex;flex-direction: row;align-items: center;font-size: 28rpx;color: #999;border-radius: 10rpx;margin: 5rpx 0 0 80rpx;
}.chat-item-name-right {display: flex;flex-direction: row;align-items: center;font-size: 28rpx;color: #999;border-radius: 10rpx;margin: 5rpx 0 0 5rpx;
}.chat-inner-msg-left {display: inline-block;flex-direction: row;align-items: center;color: #000;font-size: 30rpx;border-radius: 10rpx;background: #eee;padding: 15rpx 15rpx 15rpx 25rpx;margin-left: 12rpx;
}.chat-inner-msg-right {display: inline-block;color: #000;font-size: 30rpx;border-radius: 10rpx;background: #87EE5F;padding: 15rpx 5rpx 15rpx 15rpx;margin-right: 12rpx;
}.submit-layout {position: absolute;bottom: 0;width: 100%;background: #eee;flex-direction: row;
}.submit-layout {width: 100%;position: fixed;bottom: 0;border-top: 1px solid #ddd;padding: 10rpx 0;display: flex;flex-direction: row;align-items: center;
}.submit-input {flex: 1;background: #fff;margin: 5rpx 10rpx;border-radius: 5rpx;padding: 15rpx 20rpx;color: #333;font-size: 30rpx;
}.submit-submit {background-color: rgb(66,157,250);color: #fff;font-weight: 700;font-size: 30rpx;border-radius: 10rpx;padding: 18rpx 30rpx;margin-right: 10rpx;text-align: center;
}
/style访问效果如下 2. 登陆注册 
2.1 登陆功能实现 
客户端用户点击获取用户登录需要的code并把code和用户信息发送给服务端服务端请求微信服务器实现登陆流程如下 2.1.1 服务端提供登陆接口 
passlib 用于处理哈希密码的包支持许多安全哈希算法以及配合算法使用的实用程序推荐的算法是 Bcrypt所以终端下执行命令如下 
pip install -U bcrypt4.0.1
pip install -U passlibapi/utils.py代码 
工具函数
from passlib.context import CryptContextclass Hashing(object):def __init__(self, schemes: strbcrypt):self.crypt  CryptContext(schemes[schemes], deprecatedauto)def hash(self, raw_pwd: str) - str:密码加密:param raw_pwd: 原始密码:return: 密码的哈希值return self.crypt.hash(raw_pwd)def verify(self, raw_pwd: str, hashed_pwd: str) - bool:验证密码是否正确:param raw_pwd: 原始密码:param hashed_pwd: 密码的哈希值:return:return self.crypt.verify(raw_pwd, hashed_pwd)if __name__  __main__:hashing  Hashing()hashed_pwd  hashing.hash(123456)print(hashed_pwd) # 加密后要保存到数据库中的哈希串# 把原密码和加密后的哈希串进行配对验证通过则返回结果为Trueret  hashing.verify(123456, hashed_pwd)print(ret) 
api/scheam.py代码 
import utils
from typing import Optional
from datetime import datetime
from pydantic import BaseModel, Field, model_validatorclass UserIn(BaseModel):注册用户接口接受的数据格式username: str  Field(min_length3, max_length16)password: str  Field(min_length6, max_length16)mobile: str    Field(min_length11, max_length15)re_password: strmodel_validator(modeafter)def check_password(self):验证密码和确认密码是否一直:return: 务必返回当前对象if self.password ! self.re_password:raise ValueError(密码与确认密码不一致)# 对密码进行哈希加密hashing  utils.Hashing()self.password  hashing.hash(self.password)# 必须有数据返回否则该数据就丢失了。return selfclass UserOut(BaseModel):注册成功返回数据格式id: intusername: stravatar: Optional[str] # 允许当前字段的值为None或者strsex: boolcreated_time: datetimeupdated_time: datetimeclass LoginIn(BaseModel):登录接口接受的数据格式account: str # 账号或手机号password: str # 密码class LoginOut(BaseModel):登录成功返回数据格式code: int  # 操作结果数字表示msg: str   # 操作结果文本表示token: str # 登录凭证证明用户已经登录视图文件中编写api接口api/views.py代码 
# 导入路由类
import scheams
import models
from utils import Hashing
from fastapi import APIRouter, status, HTTPException
from tortoise.expressions import Q
# 创建一个分组路由对象
router  APIRouter()router.post(/login, status_codestatus.HTTP_201_CREATED)
async def login(user_info: scheams.LoginIn) - scheams.LoginOut:用户登录接口:param user_info: 用户登录信息:return: 登录凭证# 根据账号查询用户是否存在user  await models.User.filter(Q(usernameuser_info.account) | Q(mobileuser_info.account)).first()if not user:# 当前用户不存在raise HTTPException(status_codestatus.HTTP_422_UNPROCESSABLE_ENTITY,detail当前用户不存在或密码错误)# 密码验证hashing  Hashing()print(user.password, user_info.password)if hashing.verify(user_info.password, user.password):# 密码正确return {code: 1,msg: 登录成功,token: daskdasldasdasd;as;d;asd;as, # 根据一定的规则随机生成}# 来到这里表示密码错误raise HTTPException(status_codestatus.HTTP_422_UNPROCESSABLE_ENTITY,detail当前用户不存在或密码错误)2.1.2 客户端发送登陆请求 
代码 
templateview classcontent    view classloginBoxh3 styletext-align: center;margin-bottom:120rpx;欢迎登录/h3view classinputBoxview classiptuni-icons typecontact size24 colorrgb(66,157,250)/uni-iconsinput typetext value placeholder请输入账号//viewview classiptuni-icons typeeye size24 colorrgb(66,157,250)/uni-iconsinput typepasssword value placeholder请输入密码//viewview classiptuni-icons typecheckmarkempty size24 colorrgb(66,157,250)/uni-iconsinput typetext value placeholder请输入验证码/view classyzm验证码/view/viewbutton classlogin-btn登录/button/viewview classtipboxview classtxt —— 其他账号登录 —— /viewview classotherUserbuttonuni-icons typeqq size40 colorrgb(66,157,250)/uni-icons/buttonbutton open-typegetUserInfo getuserinfowxLoginuni-icons typeweixin size40 colorrgb(2,187,17)/uni-icons/button/view/view/view/view
/templatescript setup
const wxLogin  (e){console.log(e);console.log(微信登陆获取session_key与openid);uni.login({provider: weixin,success(response) {console.log(response);let app_id  wx3ed3b5aa5674f9ca;let app_secret  c49648af2a1659eb1711844dec39e565;let code  response.code;let url  https://api.weixin.qq.com/sns/jscode2session?appid${app_id}secret${app_secret}js_code${code}grant_typeauthorization_code;uni.request({url,success(response){console.log(response.data);}});}})
}
/scriptstyle scopedsvg {position: absolute;bottom: 0;left: 0;width: 100%;height:40%;box-sizing: border-box;display: block;background-color: #ffffff;}.loginBox{position: absolute;top: 50%;left: 50%;transform: translate(-50%,-60%);width: 90%;border-radius: 20rpx;padding: 60rpx;box-sizing: border-box;}h3{color:rgb(66,157,250);font-size: 40rpx;letter-spacing: 10rpx;margin-bottom: 40rpx;}.inputBox{}.ipt{height: 86rpx;display: flex;justify-content: flex-start;align-items: center;margin-bottom: 40rpx;background-color: #f5f5f5;border-radius: 10rpx;padding-left: 10rpx;}.ipt input{margin-left: 20rpx;font-size: 28rpx;}.ipt input{margin-left: 20rpx;}.forgetPwd{margin-top: 30rpx;font-size: 26rpx;color: #b5b5b5;text-align: end;padding:0 10rpx;display: flex;justify-content: space-between;}.login-btn{margin-top: 20rpx;line-height: 85rpx;text-align: center;background: rgb(66,157,250);border-radius: 40rpx;color: #fff;margin-top: 40rpx;}.tip{text-align: center;font-size: 28rpx;position: fixed;bottom: 50rpx;left: 50%;transform: translate(-50%,-50%);color: #f4f4f4;}.tipbox {text-align: center;margin-top: 100rpx;}.otherUser {margin-top: 30rpx;display: flex;justify-content: center;}.otherUser button{margin: 0 10px;padding: 0;height: 42px;line-height: 42px;background: transparent;border: 1px solid transparent;outline: none;}.txt {font-size: 28rpx;color: #cbcbcb;}.otherUser .uni-icons {margin-left: 20rpx;}.yzm{text-align: end;font-size: 24rpx;background: rgb(66,157,250);height: 60rpx;width: 150rpx;line-height: 60rpx;text-align: center;border-radius: 10rpx;color: #fff;}
/style2.2 注册功能实现 
2.2.1 服务端提供注册接口 
基于pydantic提供的BaseModel定义接口的输入和输出的数据结构api/scheam.py代码 
from typing import Optional
from datetime import datetime
from pydantic import BaseModel, Field, model_validatorclass UserIn(BaseModel):username: str  Field(min_length3, max_length16)password: str  Field(min_length6, max_length16)mobile: str    Field(min_length11, max_length15)re_password: strmodel_validator(modeafter)def check_password(self):验证密码和确认密码是否一直:return: 务必返回当前对象if self.password ! self.re_password:raise ValueError(密码与确认密码不一致)# 必须有数据返回否则该数据就丢失了。return selfclass UserOut(BaseModel):id: intusername: stravatar: Optional[str] # 允许当前字段的值为None或者strsex: boolcreated_time: datetimeupdated_time: datetime创建API接口的视图文件api/views.py代码 
# 导入路由类
import scheams
import models
from fastapi import APIRouter, status
# 创建一个分组路由对象
router  APIRouter()router.post(/, status_codestatus.HTTP_201_CREATED)
async def register(user_info: scheams.UserIn) - scheams.UserOut:用户注册接口:param user_info: 用户注册信息:return:user  await models.User.create(**dict(user_info))return user 
把views.py中的路由对象注册到项目中app.py代码 
from fastapi import FastAPI
from tortoise.contrib.fastapi import register_tortoise
import settings
import viewsdef init_app():app  FastAPI()# 把Tortoise-orm注册到App应用对象中register_tortoise(app,configsettings.TORTOISE_ORM,generate_schemasFalse, # 是否自动生成表结构add_exception_handlersTrue, # 是否启用自动异常处理)# 注册api接口app.include_router(views.router, prefix/user)return app2.2.2 客户端实现注册功能 
register/register.vue代码 
templateview classcontent    view classloginBoxh3 styletext-align: center;margin-bottom:120rpx;欢迎登录/h3view classinputBoxview classiptuni-icons typecontact size24 colorrgb(66,157,250)/uni-iconsinput typetext value placeholder请输入账号//viewview classiptuni-icons typeeye size24 colorrgb(66,157,250)/uni-iconsinput typepasssword value placeholder请输入密码//viewview classiptuni-icons typecheckmarkempty size24 colorrgb(66,157,250)/uni-iconsinput typetext value placeholder请输入验证码/view classyzm验证码/view/viewbutton classlogin-btn登录/button/viewview classtipboxview classtxt —— 其他账号登录 —— /viewview classotherUserbuttonuni-icons typeqq size40 colorrgb(66,157,250)/uni-icons/buttonbutton open-typegetUserInfo getuserinfowxLoginuni-icons typeweixin size40 colorrgb(2,187,17)/uni-icons/button/view/view/view/view
/templatescript setup/scriptstyle scopedsvg {position: absolute;bottom: 0;left: 0;width: 100%;height:40%;box-sizing: border-box;display: block;background-color: #ffffff;}.loginBox{position: absolute;top: 50%;left: 50%;transform: translate(-50%,-60%);width: 90%;border-radius: 20rpx;padding: 60rpx;box-sizing: border-box;}h3{color:rgb(66,157,250);font-size: 40rpx;letter-spacing: 10rpx;margin-bottom: 40rpx;}.inputBox{}.ipt{height: 86rpx;display: flex;justify-content: flex-start;align-items: center;margin-bottom: 40rpx;background-color: #f5f5f5;border-radius: 10rpx;padding-left: 10rpx;}.ipt input{margin-left: 20rpx;font-size: 28rpx;}.ipt input{margin-left: 20rpx;}.forgetPwd{margin-top: 30rpx;font-size: 26rpx;color: #b5b5b5;text-align: end;padding:0 10rpx;display: flex;justify-content: space-between;}.login-btn{margin-top: 20rpx;line-height: 85rpx;text-align: center;background: rgb(66,157,250);border-radius: 40rpx;color: #fff;margin-top: 40rpx;}.tip{text-align: center;font-size: 28rpx;position: fixed;bottom: 50rpx;left: 50%;transform: translate(-50%,-50%);color: #f4f4f4;}.tipbox {text-align: center;margin-top: 100rpx;}.otherUser {margin-top: 30rpx;display: flex;justify-content: center;}.otherUser button{margin: 0 10px;padding: 0;height: 42px;line-height: 42px;background: transparent;border: 1px solid transparent;outline: none;}.txt {font-size: 28rpx;color: #cbcbcb;}.otherUser .uni-icons {margin-left: 20rpx;}.yzm{text-align: end;font-size: 24rpx;background: rgb(66,157,250);height: 60rpx;width: 150rpx;line-height: 60rpx;text-align: center;border-radius: 10rpx;color: #fff;}
/style3. AI助理 
3.1 基于文生文实现AI会话 
pip install -U langchain
pip install -U langchain-openai3.1 服务端调用langchain对接ChatGPT 
提供接口chat/views.py代码 
import os, openai, gradio as gr
from langchain_openai import OpenAI
from langchain_openai import ChatOpenAI
from langchain.chains import ConversationChain
from langchain.memory import ConversationSummaryBufferMemory# 解决请求超时问题
os.environ[http_proxy]  http://localhost:7890
os.environ[https_proxy]  http://localhost:7890os.environ[OPENAI_API_KEY]  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
openai.api_key  os.environ[OPENAI_API_KEY]memory  ConversationSummaryBufferMemory(llmChatOpenAI(# openai_api_keysk-18q8W3BfIhs9FF6tavSBT3BlbkFJujqei0mBptIVWHQkXOvv, # 从OpenAI官方申请秘钥[可以是用户秘钥也可以是项目秘钥]# model_namegpt-3.5-turbo # 默认是gpt-3.5-turbo),max_token_limit2048
)conversation  ConversationChain(llmOpenAI(# api_keysk-18q8W3BfIhs9FF6tavSBT3BlbkFJujqei0mBptIVWHQkXOvv,max_tokens2048,temperature0.5),memorymemory,
)基于记忆体实现对话的历史上下文管理
def chat(input, history[]):history.append(input)response  conversation.predict(inputinput)history.append(response)# history[::2] 切片语法每隔两个元素提取一个元素即提取出所有的输入# history[1::2]表示从历史记录中每隔2个元素提取一个元素即提取出所有的输出# zip函数把两个列表元素打包为元组的列表的方式responses  [(u, b) for u, b in zip(history[::2], history[1::2])]print(用户输入, history[::2])print(AI回答, history[1::2])print(上下文, responses)return responses, history可视化界面中实现AI对话
with gr.Blocks(css#chatbot{height:800px} .overflow-y-auto{height:800px}) as demo:chatbot  gr.Chatbot(elem_idchatbot)state  gr.State([])with gr.Row():txt  gr.Textbox(show_labelFalse, placeholder请输入你的问题.)txt.submit(chat, [txt, state], [chatbot, state])# 启动项目
demo.launch(shareTrue) 
3.2 客户端请求服务端接口 
templateview classcontent    view classloginBoxh3 styletext-align: center;margin-bottom:120rpx;欢迎登录/h3view classinputBoxview classiptuni-icons typecontact size24 colorrgb(66,157,250)/uni-iconsinput typetext value placeholder请输入账号//viewview classiptuni-icons typeeye size24 colorrgb(66,157,250)/uni-iconsinput typepasssword value placeholder请输入密码//viewview classiptuni-icons typecheckmarkempty size24 colorrgb(66,157,250)/uni-iconsinput typetext value placeholder请输入验证码/view classyzm验证码/view/viewbutton classlogin-btn登录/button/viewview classtipboxview classtxt —— 其他账号登录 —— /viewview classotherUserbuttonuni-icons typeqq size40 colorrgb(66,157,250)/uni-icons/buttonbutton open-typegetUserInfo getuserinfowxLoginuni-icons typeweixin size40 colorrgb(2,187,17)/uni-icons/button/view/view/view/view
/templatescript setup/scriptstyle scopedsvg {position: absolute;bottom: 0;left: 0;width: 100%;height:40%;box-sizing: border-box;display: block;background-color: #ffffff;}.loginBox{position: absolute;top: 50%;left: 50%;transform: translate(-50%,-60%);width: 90%;border-radius: 20rpx;padding: 60rpx;box-sizing: border-box;}h3{color:rgb(66,157,250);font-size: 40rpx;letter-spacing: 10rpx;margin-bottom: 40rpx;}.inputBox{}.ipt{height: 86rpx;display: flex;justify-content: flex-start;align-items: center;margin-bottom: 40rpx;background-color: #f5f5f5;border-radius: 10rpx;padding-left: 10rpx;}.ipt input{margin-left: 20rpx;font-size: 28rpx;}.ipt input{margin-left: 20rpx;}.forgetPwd{margin-top: 30rpx;font-size: 26rpx;color: #b5b5b5;text-align: end;padding:0 10rpx;display: flex;justify-content: space-between;}.login-btn{margin-top: 20rpx;line-height: 85rpx;text-align: center;background: rgb(66,157,250);border-radius: 40rpx;color: #fff;margin-top: 40rpx;}.tip{text-align: center;font-size: 28rpx;position: fixed;bottom: 50rpx;left: 50%;transform: translate(-50%,-50%);color: #f4f4f4;}.tipbox {text-align: center;margin-top: 100rpx;}.otherUser {margin-top: 30rpx;display: flex;justify-content: center;}.otherUser button{margin: 0 10px;padding: 0;height: 42px;line-height: 42px;background: transparent;border: 1px solid transparent;outline: none;}.txt {font-size: 28rpx;color: #cbcbcb;}.otherUser .uni-icons {margin-left: 20rpx;}.yzm{text-align: end;font-size: 24rpx;background: rgb(66,157,250);height: 60rpx;width: 150rpx;line-height: 60rpx;text-align: center;border-radius: 10rpx;color: #fff;}
/style
 文章转载自: http://www.morning.zxgzp.cn.gov.cn.zxgzp.cn http://www.morning.jkpnm.cn.gov.cn.jkpnm.cn http://www.morning.tgyzk.cn.gov.cn.tgyzk.cn http://www.morning.pigcamp.com.gov.cn.pigcamp.com http://www.morning.qgfhr.cn.gov.cn.qgfhr.cn http://www.morning.nbwyk.cn.gov.cn.nbwyk.cn http://www.morning.rlzxr.cn.gov.cn.rlzxr.cn http://www.morning.mnccq.cn.gov.cn.mnccq.cn http://www.morning.twpq.cn.gov.cn.twpq.cn http://www.morning.shsh1688.com.gov.cn.shsh1688.com http://www.morning.leyuhh.com.gov.cn.leyuhh.com http://www.morning.fplqh.cn.gov.cn.fplqh.cn http://www.morning.rnfwx.cn.gov.cn.rnfwx.cn http://www.morning.pswzc.cn.gov.cn.pswzc.cn http://www.morning.mlpch.cn.gov.cn.mlpch.cn http://www.morning.chbcj.cn.gov.cn.chbcj.cn http://www.morning.fssjw.cn.gov.cn.fssjw.cn http://www.morning.aishuxue.com.cn.gov.cn.aishuxue.com.cn http://www.morning.ztmkg.cn.gov.cn.ztmkg.cn http://www.morning.qljxm.cn.gov.cn.qljxm.cn http://www.morning.gnjtg.cn.gov.cn.gnjtg.cn http://www.morning.dtgjt.cn.gov.cn.dtgjt.cn http://www.morning.dplmq.cn.gov.cn.dplmq.cn http://www.morning.yrlfy.cn.gov.cn.yrlfy.cn http://www.morning.pwggd.cn.gov.cn.pwggd.cn http://www.morning.ylxgw.cn.gov.cn.ylxgw.cn http://www.morning.dblfl.cn.gov.cn.dblfl.cn http://www.morning.rczrq.cn.gov.cn.rczrq.cn http://www.morning.ylyzk.cn.gov.cn.ylyzk.cn http://www.morning.sgcdr.com.gov.cn.sgcdr.com http://www.morning.jlrym.cn.gov.cn.jlrym.cn http://www.morning.rgnp.cn.gov.cn.rgnp.cn http://www.morning.pqypt.cn.gov.cn.pqypt.cn http://www.morning.lbcfj.cn.gov.cn.lbcfj.cn http://www.morning.lhhkp.cn.gov.cn.lhhkp.cn http://www.morning.wrkcw.cn.gov.cn.wrkcw.cn http://www.morning.wslr.cn.gov.cn.wslr.cn http://www.morning.nbdtdjk.cn.gov.cn.nbdtdjk.cn http://www.morning.hbywj.cn.gov.cn.hbywj.cn http://www.morning.nqmhf.cn.gov.cn.nqmhf.cn http://www.morning.tdnbw.cn.gov.cn.tdnbw.cn http://www.morning.fksrg.cn.gov.cn.fksrg.cn http://www.morning.hnkkf.cn.gov.cn.hnkkf.cn http://www.morning.nxbkw.cn.gov.cn.nxbkw.cn http://www.morning.qjxkx.cn.gov.cn.qjxkx.cn http://www.morning.qqbw.cn.gov.cn.qqbw.cn http://www.morning.lkfsk.cn.gov.cn.lkfsk.cn http://www.morning.nkcfh.cn.gov.cn.nkcfh.cn http://www.morning.wdshp.cn.gov.cn.wdshp.cn http://www.morning.kgqww.cn.gov.cn.kgqww.cn http://www.morning.lonlie.com.gov.cn.lonlie.com http://www.morning.brlgf.cn.gov.cn.brlgf.cn http://www.morning.xsbhg.cn.gov.cn.xsbhg.cn http://www.morning.jtfsd.cn.gov.cn.jtfsd.cn http://www.morning.slzkq.cn.gov.cn.slzkq.cn http://www.morning.wjlnz.cn.gov.cn.wjlnz.cn http://www.morning.zlhcw.cn.gov.cn.zlhcw.cn http://www.morning.glrzr.cn.gov.cn.glrzr.cn http://www.morning.xxiobql.cn.gov.cn.xxiobql.cn http://www.morning.wqpm.cn.gov.cn.wqpm.cn http://www.morning.dpzcc.cn.gov.cn.dpzcc.cn http://www.morning.mbqyl.cn.gov.cn.mbqyl.cn http://www.morning.cttti.com.gov.cn.cttti.com http://www.morning.stph.cn.gov.cn.stph.cn http://www.morning.cjrmf.cn.gov.cn.cjrmf.cn http://www.morning.ctbr.cn.gov.cn.ctbr.cn http://www.morning.rkxqh.cn.gov.cn.rkxqh.cn http://www.morning.qnbsx.cn.gov.cn.qnbsx.cn http://www.morning.qwfl.cn.gov.cn.qwfl.cn http://www.morning.pinngee.com.gov.cn.pinngee.com http://www.morning.fkgqn.cn.gov.cn.fkgqn.cn http://www.morning.jltmb.cn.gov.cn.jltmb.cn http://www.morning.mhnb.cn.gov.cn.mhnb.cn http://www.morning.hzqjgas.com.gov.cn.hzqjgas.com http://www.morning.yltyz.cn.gov.cn.yltyz.cn http://www.morning.gwxwl.cn.gov.cn.gwxwl.cn http://www.morning.wgrl.cn.gov.cn.wgrl.cn http://www.morning.dppfh.cn.gov.cn.dppfh.cn http://www.morning.lswgs.cn.gov.cn.lswgs.cn http://www.morning.yubkwd.cn.gov.cn.yubkwd.cn