2024山东大学软件学院创新实训——智慧医疗问答系统(六)

后台管理系统

主要负责后台管理系统中的用户基本信息管理和用户反馈信息管理两部分内容,完成了基本的增删改查和数据统计功能

目录

后台管理系统

一、MySQL数据库

1. 用户基本信息表

2. 用户反馈信息表

3. 历史记录表

二、内网穿透工具

1. 登录后选择内网映射服务

2. 点击添加映射

3. 选择应用场景

4. 填写内网地址(端口为3306)

5. 创建映射完成

三、后端接口设计

1. ChatGLM对话

2. 显示所有用户信息

3. 根据id查询用户信息

4. 修改用户信息

5. 删除用户信息

6. 重置密码

7. 实时在线人数

8. 年龄分布

9. 地区分布

10. 十日用户活跃度

11. 修改反馈信息

12. 反馈类别统计

四、接口文档


一、MySQL数据库

首先新建一个连接                                                                                                                                                                                       

在数据库model下共建立了四个表,分别是records、user_feedback、user_info和user_record(联系表)

1. 用户基本信息表

2. 用户反馈信息表

3. 历史记录表

数据库建表完成

二、内网穿透工具

下载地址:路由侠-局域网变公网 (luyouxia.com)

使用路由侠添加映射:

1. 登录后选择内网映射服务

2. 点击添加映射

3. 选择应用场景

4. 填写内网地址(端口为3306)

5. 创建映射完成

得到映射后的外网地址后,在远程服务器访问MySQL数据库:

class DBUtils(object):
    # 初始化连接对象和游标对象
    _db_conn = None
    _db_cursor = None

    """
        数据库工具类 初始化方法
        传入 host,user,password,db 进行数据库连接
    """

    def __init__(self, host, user, password, db, port=xxxx, charset='utf8mb4'):
        try:
            self._db_conn = pymysql.connect(host=host, user=user, password=password, port=port, db=db, charset=charset)
            self._db_cursor = self._db_conn.cursor()
        except Exception as e:
            logging.error(e)
def get_db_conn():
    """
    获取数据库连接
    :return: db_conn 数据库连接对象
    """
    return DBUtils(host='公网地址', user='root', password='12345', db='model')

三、后端接口设计

首先在数据库工具类dbutils.py中实现数据库连接、基本增删改查操作、执行SQL查询并返回结果,其主要作用是为了简化与数据库交互的过程

1. ChatGLM对话
@app.route('/api/dialogue', methods=['POST'])
def create_item():
    global model, tokenizer
    
    # 解析JSON数据
    json_post_raw = request.json
    json_post = json.dumps(json_post_raw)
    json_post_list = json.loads(json_post)
    prompt = json_post_list.get('prompt')
    history = json_post_list.get('history')
    max_length = json_post_list.get('max_length')
    top_p = json_post_list.get('top_p')
    temperature = json_post_list.get('temperature')
    
    # 调用模型生成回答
    response, history = model.chat(tokenizer,
                                   prompt,
                                   history=history,
                                   max_length=max_length if max_length else 2048,
                                   top_p=top_p if top_p else 0.7,
                                   temperature=temperature if temperature else 0.95)
    
    # 记录时间
    now = datetime.datetime.now()
    time = now.strftime("%Y-%m-%d %H:%M:%S")
    
    # 准备返回数据
    answer = {
        "response": response,
        "history": history,
        "status": 200,
        "time": time
    }
    
    # 打印日志
    log = "[" + time + "] " + '", prompt:"' + prompt + '", response:"' + repr(response) + '"'
    print(log)
    
    # 清理缓存
    if torch.backends.mps.is_available():
        torch.mps.empty_cache()
    
    # 返回JSON响应
    return jsonify(answer)
2. 显示所有用户信息
@app.route('/api/users', methods=['GET'])
def get_users():
    """
    获取所有用户数据
    :return: JSON格式的响应,包含所有用户数据
    """
    db_conn = get_db_conn()
    try:
        users = db_conn.get_all(sql_str="select * from user_info")
        if not users:
            return msg(201,'User not found')  # 假设201表示数据为空或没有找到数据
        # 将 datetime 字段转换为字符串  
        for user in users:  
            if 'update_time' in user:  # 确保 'time' 字段存在  
                user['update_time'] = user['update_time'].isoformat() if user['update_time'] is not None else None
            if 'create_time' in user:  # 确保 'time' 字段存在  
                user['create_time'] = user['create_time'].isoformat() if user['create_time'] is not None else None
        return msg(200, users)  # 假设200表示成功
    except Exception as e:
        print(e)
        return msg(500, 'Internal Server Error')  # 假设500表示服务器内部错误
3. 根据id查询用户信息
@app.route('/api/users/<user_id>', methods=['GET'])
def get_user_by_id(user_id):
    """
    根据用户 ID 获取用户数据
    :param user_id: 用户的唯一标识符
    :return: JSON 格式的响应,包含用户数据
    """
    db_conn = get_db_conn()
    try:
        # 使用用户 ID 构建查询语句
        user = db_conn.get_one(sql_str="select * from user_info where user_id = %s", args=(user_id,))
        
        if not user:
            return msg(201,'User not found')  # 假设201表示数据为空或没有找到数据
        # 将 datetime 字段转换为字符串  
     
        if 'update_time' in user:  # 确保 'time' 字段存在  
            user['update_time'] = user['update_time'].isoformat() if user['update_time'] is not None else None
        if 'create_time' in user:  # 确保 'time' 字段存在  
            user['create_time'] = user['create_time'].isoformat() if user['create_time'] is not None else None
        # 假设 get_all 方法返回的是字典列表
        return msg(200, user)  # 返回第一个(也是唯一一个)用户数据
        
    except Exception as e:
        print(e)
        return msg(500, 'Internal Server Error')  # 假设500表示服务器内部错误
4. 修改用户信息
@app.route('/api/users', methods=['PUT'])
def update_user():
    """
    根据用户 ID 修改用户信息
    :param user_id: 用户的唯一标识符
    :return: JSON 格式的响应,表示更新操作的结果
    """
    db_conn = get_db_conn()
    try:
        # 解析请求体中的 JSON 数据
        user_data = request.json
        # 确保用户 ID 在请求中
        if 'user_id' not in user_data:
            return msg(404, 'User ID is required') 
        
        user_id = user_data['user_id']
        updates = [f"{key} = %s" for key in user_data if key != 'user_id']  # 排除 user_id
        sql_str = "UPDATE user_info SET " + ", ".join(updates) + " WHERE user_id = %s"
        
        # 构建参数列表,包含所有要更新的列的值和 user_id
        args = [user_data[key] for key in user_data if key != 'user_id'] + [user_id]
        
        # 执行更新操作
        result = db_conn.update(sql_str, args=args)
        
        # 检查更新是否成功
        if result:
            return msg(200,"修改成功")
        else:
            return msg(201,'User not found') 
        
    except Exception as e:
        print(e)
        return msg(500, 'Internal Server Error') 
5. 删除用户信息
@app.route('/api/users', methods=['DELETE'])  
def delete_user_by_id():  
    """  
    根据用户 ID 删除用户数据  
    :param user_id: 用户的唯一标识符  
    :return: JSON 格式的响应,包含操作结果  
    """  
    db_conn = get_db_conn()  
    try:  
       # 从 URL 查询参数中获取 user_id  
        user_id = request.args.get('user_id')  
        if not user_id:  
            return msg(404, 'User ID is required')
        
        # 使用用户 ID 构建删除语句  
        affected_rows = db_conn.delete(sql_str="delete from user_info where user_id = %s", args=(user_id,))  
          
        if affected_rows == 0:  
            return msg(201, 'User not found')  # 假设201表示用户未找到  
          
        return msg(200,"删除成功")  # 200(成功删除)  
          
    except Exception as e:  
        print(e)  
        return msg(500, 'Internal Server Error')  # 假设500表示服务器内部错误 
6. 重置密码
@app.route('/api/users/reset_password', methods=['POST'])
def reset_user_password():
    """
    根据用户 ID 重置用户密码
    :param user_id: 用户的唯一标识符
    :param new_password: 新密码
    :return: JSON 格式的响应,包含操作结果
    """
    db_conn = get_db_conn()
    try:
        # 从请求体中获取 user_id 和 new_password
        user_id = request.json.get('user_id')
        new_password = request.json.get('new_password')
        if not user_id or not new_password:
            return msg(404, 'User ID and new password are required')

        # 使用用户 ID 构建更新密码的语句
        affected_rows = db_conn.update(
            "UPDATE user_info SET password = %s WHERE user_id = %s",
            (new_password, user_id)
        )

        if affected_rows == 0:
            return msg(201, 'User not found')

        return msg(200, "密码重置成功")

    except Exception as e:
        print(e)
        return msg(500, 'Internal Server Error')
7. 实时在线人数
@app.route('/api/users/online_count', methods=['GET'])
def get_online_user_count():
    db_conn = get_db_conn()
    try:
        # 定义在线状态的时间窗口,这里设置为5分钟
        time_threshold = datetime.now() - timedelta(minutes=5)
        
        # 构建查询在线用户数量的SQL语句
        query = """
        SELECT COUNT(*) FROM user_info
        WHERE update_time >= %s
        """
        cursor = db_conn._db_cursor
        cursor.execute(query, (time_threshold,))
        count = cursor.fetchone()[0]
        
        return msg(200, {'online_user_count': count})

    except Exception as e:
        print(e)
        return msg(500, 'Internal Server Error')
8. 年龄分布
@app.route('/api/users/age_distribution', methods=['GET'])  
def get_age_distribution():  
    db_conn = get_db_conn()  
    try:  
        # 定义年龄段范围  
        age_ranges = [(i, i + 9) for i in range(0, 101, 10)]  # 0-9, 10-19, ..., 100-109  
          
        # 初始化结果字典  
        age_distribution = {f"{start}-{end}": 0 for start, end in age_ranges}  
          
        # 假设你的用户信息表叫user_info,且有一个age字段存储用户年龄  
        query = """  
        SELECT age FROM user_info  
        """  
        cursor = db_conn._db_cursor
        cursor.execute(query)
        results = cursor.fetchall()  
          
        # 遍历用户年龄,更新年龄段统计  
        for age, in results:  
            for range_start, range_end in age_ranges:  
                if age >= range_start and age <= range_end:  
                    age_distribution[f"{range_start}-{range_end}"] += 1  
                    break  # 跳出内层循环,因为已经找到了匹配的年龄段  
          
        return msg(200,age_distribution)  
  
    except Exception as e:  
        print(e)  
        return msg(500, 'Internal Server Error')
9. 地区分布
@app.route('/api/users/region_distribution', methods=['GET'])  
def get_region_heatmap():  
    db_conn = get_db_conn()  
    try:  
        # 假设address字段的格式是“省份名称+城市名称”,我们使用SUBSTRING_INDEX函数来提取省份  
        query = """  
        SELECT   
            SUBSTRING_INDEX(address, '省', 1) AS province, -- 提取省份部分  
            COUNT(*) AS count  
        FROM   
            user_info  
        GROUP BY   
            province  
        HAVING   
            province IS NOT NULL AND TRIM(province) != '' 
        ORDER BY   
            count DESC; -- 可以按人数降序排列,根据需要调整  
        """  
          
        cursor = db_conn._db_cursor
        cursor.execute(query)  
        regions = cursor.fetchall()  
          
        # 转换查询结果为字典列表  
        region_data = [{'province': region[0], 'count': region[1]} for region in regions]  
          
        return msg(200,region_data)  
  
    except Exception as e:  
        print(e)  
        return msg(500, 'Internal Server Error')
10. 十日用户活跃度
@app.route('/api/users/activity', methods=['GET'])  
def get_user_activity():  
    db_conn = get_db_conn()  
    try:  
        # 计算最近10天的日期列表  
        today = datetime.utcnow().date()  
        dates = [(today - timedelta(days=i)).strftime('%Y-%m-%d') for i in range(10)]  
        activity = {date: 0 for date in dates}  
  
        query = """  
        SELECT DATE(update_time) AS login_date
        FROM user_info 
        WHERE update_time >= DATE_SUB(%s, INTERVAL 10 DAY)  
        GROUP BY login_date  
        """  
  
        # 使用数据库连接执行查询  
        cursor = db_conn._db_cursor # 假设db_conn有一个cursor属性  
        cursor.execute(query, (today,))  
        results = cursor.fetchall()  
        
        print(results)
  
        # 遍历查询结果,并更新活动字典  
        for row in results:  
            login_date = row[0].strftime('%Y-%m-%d') 
            print(login_date)
            if login_date in activity:  
                activity[login_date] += 1  # 增加该日期的用户登录数  
  
        # 返回结果  
        return msg(200, activity)  
  
    except Exception as e:  
        print(e)  
        return msg(500, 'Internal Server Error')  
11. 修改反馈信息
@app.route('/api/users/feedback/mark-as-read', methods=['PUT'])  
def mark_feedback_as_read():  
    """  
    将指定反馈ID的反馈标记为已读(status从0变为1)  
    :param feedback_id: 反馈的唯一标识符  
    :return: JSON 格式的响应,表示更新操作的结果  
    """  
    db_conn = get_db_conn()  
    try:  
        # 解析请求体中的 JSON 数据
        feedback_data = request.json
        # 确保用户 ID 在请求中
        if 'feedback_id' not in feedback_data:
            return msg(404, 'Feedback ID is required') 
        feedback_id = feedback_data['feedback_id']
        # 构造SQL更新语句  
        sql_str = "UPDATE user_feedback SET status = 1 WHERE feedback_id = %s"  
        # 准备参数列表  
        args = (feedback_id,)  
          
        # 执行更新操作  
        result = db_conn.update(sql_str, args)  
          
        # 检查更新是否成功  
        # 在真实环境中,result应该表示受影响的行数,例如返回True表示成功,False表示失败或未找到  
        if result:  
            return msg(200, "反馈已标记为已读")  
        else:  
            return msg(204, "反馈未找到或未发生更改")  
          
    except Exception as e:  
        print(e)  
        return msg(500, 'Internal Server Error')  
12. 反馈类别统计
@app.route('/api/users/feedback/type', methods=['GET'])  
def get_feedback_type():  
    db_conn = get_db_conn()  
    try:  
        # 构造SQL查询语句来统计不同反馈类别的数量  
        query = """  
        SELECT     
            type,  -- 反馈类别  
            COUNT(*) AS count  -- 该类别的数量  
        FROM     
            user_feedback  -- 假设的反馈信息表名  
        GROUP BY     
            type  -- 按反馈类别分组  
        ORDER BY     
            count DESC;  -- 可以按数量降序排列,根据需要调整  
        """  
          
        # 假设cursor是数据库连接对象的属性,用于执行查询  
        cursor = db_conn._db_cursor  
        cursor.execute(query)  
        types_with_counts = cursor.fetchall()  
          
        # 转换查询结果为字典列表  
        type_distribution = [{'type': type_count[0], 'count': type_count[1]} for type_count in types_with_counts]  
          
        return msg(200, type_distribution)  
      
    except Exception as e:  
        print(e)  
        return msg(500, 'Internal Server Error')

四、接口文档

下载地址:RunApi (showdoc.cc)

使用在线的项目文档工具showDoc和RunApi进行接口的测试与文档的编写

Runapi是一个以接口为核心的开发测试工具(可以看做是Postman的精简版)。目前客户端支持win、mac、linux平台和在线版 ,包含接口测试、自动流程测试、Mock数据、项目协作等功能。

单纯的Runapi和Postman相比优势并不大,而与showdoc配合使用效率比较显著,用runapi测试接口的同时它将自动生成API文档到showdoc,也可共用showdoc的团队管理机制实现多人协作。

Runapi客户端可以创建带调试的API接口文档、或者Markdown格式的文档。

1. ChatGLM对话

2. 显示所有用户信息

3. 根据id查询用户信息

4. 修改用户信息

5. 删除用户信息

6. 重置密码

7. 实时在线人数

8. 年龄分布

9. 地区分布

10. 十日用户活跃度

11. 修改反馈信息

12. 反馈类别统计

  • 23
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值