diff --git a/doc/408_01_AI日记心情辅助系统_行业和领域调研分析报告.doc b/doc/408_01_AI日记心情辅助系统_行业和领域调研分析报告.doc new file mode 100644 index 0000000..d83da87 Binary files /dev/null and b/doc/408_01_AI日记心情辅助系统_行业和领域调研分析报告.doc differ diff --git a/doc/408_02_AI日记心情辅助系统系统_需求构思及描述文档.doc b/doc/408_02_AI日记心情辅助系统系统_需求构思及描述文档.doc new file mode 100644 index 0000000..5fd09e8 Binary files /dev/null and b/doc/408_02_AI日记心情辅助系统系统_需求构思及描述文档.doc differ diff --git a/doc/408_03_AI日记心情辅助系统系统_需求规格说明书.doc b/doc/408_03_AI日记心情辅助系统系统_需求规格说明书.doc new file mode 100644 index 0000000..9ed24dd Binary files /dev/null and b/doc/408_03_AI日记心情辅助系统系统_需求规格说明书.doc differ diff --git a/doc/408_04_AI日记心情辅助系统系统_设计规格说明书.doc b/doc/408_04_AI日记心情辅助系统系统_设计规格说明书.doc new file mode 100644 index 0000000..c1b2e28 Binary files /dev/null and b/doc/408_04_AI日记心情辅助系统系统_设计规格说明书.doc differ diff --git a/model/408_01_AI日记心情辅助系统_需求模型.doc b/model/408_01_AI日记心情辅助系统_需求模型.doc new file mode 100644 index 0000000..d4d8016 Binary files /dev/null and b/model/408_01_AI日记心情辅助系统_需求模型.doc differ diff --git a/model/408_02_AI日记心情辅助系统系统_设计模型.doc b/model/408_02_AI日记心情辅助系统系统_设计模型.doc new file mode 100644 index 0000000..7fa1b08 Binary files /dev/null and b/model/408_02_AI日记心情辅助系统系统_设计模型.doc differ diff --git a/other/408_05_AI日记心情辅助系统系统_软件工程课程设计汇报.ppt b/other/408_05_AI日记心情辅助系统系统_软件工程课程设计汇报.ppt new file mode 100644 index 0000000..8dacb55 Binary files /dev/null and b/other/408_05_AI日记心情辅助系统系统_软件工程课程设计汇报.ppt differ diff --git a/other/408_06_AI日记心情辅助系统系统_软件开发项目的个人自评报告.xlsx b/other/408_06_AI日记心情辅助系统系统_软件开发项目的个人自评报告.xlsx new file mode 100644 index 0000000..6ff6c1f Binary files /dev/null and b/other/408_06_AI日记心情辅助系统系统_软件开发项目的个人自评报告.xlsx differ diff --git a/other/408_07_AI日记心情辅助系统系统_软件开发项目的团队自评报告.xlsx b/other/408_07_AI日记心情辅助系统系统_软件开发项目的团队自评报告.xlsx new file mode 100644 index 0000000..8bf79e2 Binary files /dev/null and b/other/408_07_AI日记心情辅助系统系统_软件开发项目的团队自评报告.xlsx differ diff --git a/other/408_08_AI日记心情辅助系统系统_230340254李林芮_实践总结报告.doc b/other/408_08_AI日记心情辅助系统系统_230340254李林芮_实践总结报告.doc new file mode 100644 index 0000000..2081b20 Binary files /dev/null and b/other/408_08_AI日记心情辅助系统系统_230340254李林芮_实践总结报告.doc differ diff --git a/other/408_08_AI日记心情辅助系统系统_230340269吴丽丽_实践总结报告.doc b/other/408_08_AI日记心情辅助系统系统_230340269吴丽丽_实践总结报告.doc new file mode 100644 index 0000000..34d1e03 Binary files /dev/null and b/other/408_08_AI日记心情辅助系统系统_230340269吴丽丽_实践总结报告.doc differ diff --git a/other/408_08_AI日记心情辅助系统系统_230340272王宇佳_实践总结报告.doc b/other/408_08_AI日记心情辅助系统系统_230340272王宇佳_实践总结报告.doc new file mode 100644 index 0000000..9b7bf7f Binary files /dev/null and b/other/408_08_AI日记心情辅助系统系统_230340272王宇佳_实践总结报告.doc differ diff --git a/other/408_08_AI日记心情辅助系统系统_230340275杨佩蕾_实践总结报告.doc b/other/408_08_AI日记心情辅助系统系统_230340275杨佩蕾_实践总结报告.doc new file mode 100644 index 0000000..57b6778 Binary files /dev/null and b/other/408_08_AI日记心情辅助系统系统_230340275杨佩蕾_实践总结报告.doc differ diff --git a/other/408_09_AI日记心情辅助系统系统_演示录屏.mp4 b/other/408_09_AI日记心情辅助系统系统_演示录屏.mp4 new file mode 100644 index 0000000..8b68cc0 Binary files /dev/null and b/other/408_09_AI日记心情辅助系统系统_演示录屏.mp4 differ diff --git a/other/408_10_AI日记心情辅助系统系统_宣传海报.jpg b/other/408_10_AI日记心情辅助系统系统_宣传海报.jpg new file mode 100644 index 0000000..c150067 Binary files /dev/null and b/other/408_10_AI日记心情辅助系统系统_宣传海报.jpg differ diff --git a/src/AiDiary/.env b/src/AiDiary/.env new file mode 100644 index 0000000..6fade1d --- /dev/null +++ b/src/AiDiary/.env @@ -0,0 +1,2 @@ +# 复制此文件为.env并填入你的API密钥 +DEEPSEEK_API_KEY=sk-2685bfd0fe054c9f82a12ac7905dd80d \ No newline at end of file diff --git a/src/AiDiary/.env.example b/src/AiDiary/.env.example new file mode 100644 index 0000000..8050653 --- /dev/null +++ b/src/AiDiary/.env.example @@ -0,0 +1,2 @@ +# 复制此文件为.env并填入你的API密钥 +DEEPSEEK_API_KEY=sk-2685bfd0fe054c9f82a12ac7905dd80d diff --git a/src/AiDiary/.idea/.gitignore b/src/AiDiary/.idea/.gitignore new file mode 100644 index 0000000..359bb53 --- /dev/null +++ b/src/AiDiary/.idea/.gitignore @@ -0,0 +1,3 @@ +# 默认忽略的文件 +/shelf/ +/workspace.xml diff --git a/src/AiDiary/.idea/PythonProject2.iml b/src/AiDiary/.idea/PythonProject2.iml new file mode 100644 index 0000000..d273b7c --- /dev/null +++ b/src/AiDiary/.idea/PythonProject2.iml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/AiDiary/.idea/inspectionProfiles/profiles_settings.xml b/src/AiDiary/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/src/AiDiary/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/src/AiDiary/.idea/misc.xml b/src/AiDiary/.idea/misc.xml new file mode 100644 index 0000000..676146d --- /dev/null +++ b/src/AiDiary/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/src/AiDiary/.idea/modules.xml b/src/AiDiary/.idea/modules.xml new file mode 100644 index 0000000..1c41000 --- /dev/null +++ b/src/AiDiary/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/src/AiDiary/.idea/vcs.xml b/src/AiDiary/.idea/vcs.xml new file mode 100644 index 0000000..d843f34 --- /dev/null +++ b/src/AiDiary/.idea/vcs.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/AiDiary/TestDeepSeek.py b/src/AiDiary/TestDeepSeek.py new file mode 100644 index 0000000..089def9 --- /dev/null +++ b/src/AiDiary/TestDeepSeek.py @@ -0,0 +1,17 @@ +#OpenAI SDK +# Please install OpenAI SDK first: `pip3 install openai` + +from openai import OpenAI + +client = OpenAI(api_key="sk-2685bfd0fe054c9f82a12ac7905dd80d", base_url="https://api.deepseek.com") + +response = client.chat.completions.create( + model="deepseek-chat", + messages=[ + {"role": "system", "content": "你是一个情绪分析大师,请用委婉的语气说话"}, + {"role": "user", "content": "输入日记内容"}, + ], + stream=False +) + +print(response.choices[0].message.content) \ No newline at end of file diff --git a/src/AiDiary/check_db.py b/src/AiDiary/check_db.py new file mode 100644 index 0000000..bce362f --- /dev/null +++ b/src/AiDiary/check_db.py @@ -0,0 +1,24 @@ +import sqlite3 + +# 连接到数据库 +try: + conn = sqlite3.connect('database.db') + c = conn.cursor() + + # 查看所有表 + print("数据库中的表:") + c.execute("SELECT name FROM sqlite_master WHERE type='table';") + tables = c.fetchall() + for table in tables: + print(f"- {table[0]}") + + # 查看表结构 + print(f" {table[0]}表结构:") + c.execute(f"PRAGMA table_info({table[0]});") + columns = c.fetchall() + for column in columns: + print(f" - {column[1]} ({column[2]})") + + conn.close() +except Exception as e: + print(f"检查数据库出错: {e}") \ No newline at end of file diff --git a/src/AiDiary/database.db b/src/AiDiary/database.db new file mode 100644 index 0000000..b5c5500 Binary files /dev/null and b/src/AiDiary/database.db differ diff --git a/src/AiDiary/main.py b/src/AiDiary/main.py new file mode 100644 index 0000000..b161e23 --- /dev/null +++ b/src/AiDiary/main.py @@ -0,0 +1,1016 @@ +from flask import Flask, render_template, request, redirect, url_for, session, flash, jsonify, send_file +from flask_cors import CORS +from functools import wraps +from openai import OpenAI +import json +import markdown +import sqlite3 +import hashlib +from datetime import datetime +import os +import re +from flask import send_from_directory +import random + +# 初始化Flask应用 +app = Flask(__name__) +app.secret_key = 'your-secret-key-here' # 添加密钥用于session +CORS(app) + + +# 提供mypo文件夹中的图片访问 +@app.route('/mypo/') +def serve_mypo_file(filename): + return send_from_directory('mypo', filename) + + +# 登录验证装饰器 +def login_required(func): + @wraps(func) + def decorated_function(*args, **kwargs): + if 'user_id' not in session: + flash('请先登录!', 'error') + return redirect(url_for('login')) + return func(*args, **kwargs) + + return decorated_function + + +# 配置DeepSeek API +DEEPSEEK_API_KEY = "sk-2685bfd0fe054c9f82a12ac7905dd80d" +client = OpenAI( + api_key=DEEPSEEK_API_KEY, + base_url="https://api.deepseek.com" +) + + +# 数据库初始化 +def init_db(): + conn = sqlite3.connect('database.db') + c = conn.cursor() + + # 用户表 + c.execute('''CREATE TABLE IF NOT EXISTS users + (id INTEGER PRIMARY KEY AUTOINCREMENT, + username TEXT UNIQUE NOT NULL, + password TEXT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)''') + + # 日记表 + c.execute('''CREATE TABLE IF NOT EXISTS diaries + (id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER NOT NULL, + title TEXT NOT NULL, + content TEXT NOT NULL, + tags TEXT, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (user_id) REFERENCES users(id))''') + + # 情绪分析结果表 + c.execute('''CREATE TABLE IF NOT EXISTS emotion_analyses + (id INTEGER PRIMARY KEY AUTOINCREMENT, + diary_id INTEGER NOT NULL, + dominant_emotion TEXT NOT NULL, + analysis TEXT NOT NULL, + emotion_scores TEXT NOT NULL, + suggestion TEXT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (diary_id) REFERENCES diaries(id) ON DELETE CASCADE)''') + + # 收藏卡片表 + c.execute('''CREATE TABLE IF NOT EXISTS collected_cards + (id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER NOT NULL, + card_name TEXT NOT NULL, + card_type TEXT NOT NULL, + card_content TEXT NOT NULL, + card_image_url TEXT NOT NULL, + collected_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (user_id) REFERENCES users(id))''') + # 周报表 + c.execute('''CREATE TABLE IF NOT EXISTS weekly_reports + (id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER NOT NULL, + title TEXT NOT NULL, + content TEXT NOT NULL, + diary_count INTEGER NOT NULL, + date_range TEXT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (user_id) REFERENCES users(id))''') + + conn.commit() + conn.close() + + +# 密码哈希函数 +def hash_password(password): + return hashlib.sha256(password.encode()).hexdigest() + + +# 主页 +@app.route('/') +def index(): + return render_template('index.html') + + +# 情绪分析器页面已在下方使用login_required装饰器定义 +# 为避免重复路由定义,此处已移除 + +# 用户注册 +@app.route('/register', methods=['GET', 'POST']) +def register(): + if request.method == 'POST': + username = request.form['username'] + password = request.form['password'] + + conn = sqlite3.connect('database.db') + c = conn.cursor() + + try: + c.execute('INSERT INTO users (username, password) VALUES (?, ?)', + (username, hash_password(password))) + conn.commit() + flash('注册成功!请登录。', 'success') + return redirect(url_for('login')) + except sqlite3.IntegrityError: + flash('用户名已存在!', 'error') + finally: + conn.close() + + return render_template('register.html') + + +# 用户登录 +@app.route('/login', methods=['GET', 'POST']) +def login(): + if request.method == 'POST': + username = request.form['username'] + password = request.form['password'] + + conn = sqlite3.connect('database.db') + c = conn.cursor() + c.execute('SELECT * FROM users WHERE username = ? AND password = ?', + (username, hash_password(password))) + user = c.fetchone() + conn.close() + + if user: + session['user_id'] = user[0] + session['username'] = user[1] + flash('登录成功!', 'success') + return redirect(url_for('index')) + else: + flash('用户名或密码错误!', 'error') + + return render_template('login.html') + + +# 用户登出 + +@app.route('/logout') +def logout(): + session.clear() + flash('已成功登出!', 'info') + return redirect(url_for('index')) + + +# 个人中心 +@app.route('/profile') +@login_required +def profile(): + conn = sqlite3.connect('database.db') + c = conn.cursor() + c.execute('SELECT created_at FROM users WHERE id = ?', (session['user_id'],)) + created_at = c.fetchone()[0] + conn.close() + + # 格式化日期显示 + created_at_obj = datetime.strptime(created_at, '%Y-%m-%d %H:%M:%S') + formatted_date = created_at_obj.strftime('%Y年%m月%d日') + + return render_template('profile.html', created_at=formatted_date) + + +# 修改密码 +@app.route('/change_password', methods=['POST']) +@login_required +def change_password(): + old_password = request.form['old_password'] + new_password = request.form['new_password'] + + conn = sqlite3.connect('database.db') + c = conn.cursor() + c.execute('SELECT password FROM users WHERE id = ?', (session['user_id'],)) + current_password = c.fetchone()[0] + + if hash_password(old_password) == current_password: + c.execute('UPDATE users SET password = ? WHERE id = ?', + (hash_password(new_password), session['user_id'])) + conn.commit() + flash('密码修改成功!', 'success') + else: + flash('原密码错误!', 'error') + + conn.close() + return redirect(url_for('profile')) + + +# 情绪分析功能 +# def analyze_emotion(content): +# """调用DeepSeek API进行情绪分析""" +# try: +# system_prompt = """ +# 你是一个专业的情绪分析专家。请分析用户提供的的日记内容,并返回以下信息: +# 1. 主要情绪(积极、消极、中性或其他具体情绪) +# 2. 情绪分析说明(简要解释为什么是这种情绪) +# 3. 情绪得分(以JSON格式返回各种情绪的百分比) +# 4. 三条适合的建议或回应 +# 5.500字 +# +# 请将结果以JSON格式返回,包含dominant_emotion、analysis、emotion_scores和suggestion四个字段。 +# """ +# +# response = client.chat.completions.create( +# model="deepseek-chat", +# messages=[ +# {"role": "system", "content": system_prompt}, +# {"role": "user", "content": content} +# ], +# stream=False +# ) +# +# result = json.loads(response.choices[0].message.content) +# return result +# +# except Exception as e: +# print(f"情绪分析出错: {str(e)}") +# return { +# "error": f"分析失败: {str(e)}", +# "dominant_emotion": "未知", +# "analysis": "无法分析情绪", +# "emotion_scores": {"未知": 1.0}, +# "suggestion": "请尝试重新输入内容" +# } + +# 保存周报 +@app.route('/save_weekly_report', methods=['POST']) +@login_required +def save_weekly_report(): + data = request.get_json() + + if not data or 'title' not in data or 'content' not in data: + return jsonify({"error": "缺少必要参数"}), 400 + + title = data['title'] + content = data['content'] + diary_count = data.get('diary_count', 1) + date_range = data.get('date_range', '') + + conn = sqlite3.connect('database.db') + c = conn.cursor() + + try: + c.execute('''INSERT INTO weekly_reports + (user_id, title, content, diary_count, date_range) + VALUES (?, ?, ?, ?, ?)''', + (session['user_id'], title, content, diary_count, date_range)) + conn.commit() + + report_id = c.lastrowid + return jsonify({ + "success": True, + "message": "报告已成功保存!", + "report_id": report_id + }) + + except Exception as e: + print(f"保存周报失败: {str(e)}") + return jsonify({"error": "保存报告失败"}), 500 + finally: + conn.close() + + +# 获取用户的所有周报 +@app.route('/get_weekly_reports') +@login_required +def get_weekly_reports(): + conn = sqlite3.connect('database.db') + c = conn.cursor() + + c.execute('''SELECT id, title, content, diary_count, date_range, created_at + FROM weekly_reports + WHERE user_id = ? + ORDER BY created_at DESC''', + (session['user_id'],)) + + reports = c.fetchall() + conn.close() + + formatted_reports = [] + for report in reports: + created_at = datetime.strptime(report[5], '%Y-%m-%d %H:%M:%S') + formatted_reports.append({ + 'id': report[0], + 'title': report[1], + 'content': report[2], + 'diary_count': report[3], + 'date_range': report[4], + 'created_at': created_at.strftime('%Y-%m-%d %H:%M') + }) + + return jsonify(formatted_reports) + + +# 获取单个周报详情 +@app.route('/get_weekly_report/') +@login_required +def get_weekly_report(report_id): + conn = sqlite3.connect('database.db') + c = conn.cursor() + + # 验证报告属于当前用户 + c.execute('''SELECT id, title, content, diary_count, date_range, created_at + FROM weekly_reports + WHERE id = ? AND user_id = ?''', + (report_id, session['user_id'])) + + report = c.fetchone() + conn.close() + + if not report: + return jsonify({"error": "报告不存在或您没有权限访问"}), 404 + + created_at = datetime.strptime(report[5], '%Y-%m-%d %H:%M:%S') + + return jsonify({ + 'id': report[0], + 'title': report[1], + 'content': report[2], + 'diary_count': report[3], + 'date_range': report[4], + 'created_at': created_at.strftime('%Y-%m-%d %H:%M') + }) + + +# 删除周报 +@app.route('/delete_weekly_report/', methods=['DELETE']) +@login_required +def delete_weekly_report(report_id): + conn = sqlite3.connect('database.db') + c = conn.cursor() + + # 验证报告属于当前用户 + c.execute('SELECT id FROM weekly_reports WHERE id = ? AND user_id = ?', + (report_id, session['user_id'])) + report = c.fetchone() + + if not report: + conn.close() + return jsonify({"error": "报告不存在或您没有权限删除"}), 404 + + try: + c.execute('DELETE FROM weekly_reports WHERE id = ?', (report_id,)) + conn.commit() + conn.close() + + return jsonify({"success": True, "message": "报告已删除"}) + + except Exception as e: + print(f"删除周报失败: {str(e)}") + conn.close() + return jsonify({"error": "删除报告失败"}), 500 + + +# 生成周报分析(流式版本) +@app.route('/generate_weekly_report_stream', methods=['POST']) +@login_required +def generate_weekly_report_stream(): + data = request.get_json() + + if not data or 'content' not in data: + return jsonify({"error": "请提供日记内容"}), 400 + + try: + diary_count = data.get('diary_count', 1) + content = data['content'] + + # 调用DeepSeek API生成流式情绪分析 + system_prompt = f""" + 请根据用户提供的{diary_count}篇日记内容,生成一段详细的情感分析和建议。 + 要求: + 1. 详细分析,控制在500-800字左右 + 2. 包含情绪状态分析、趋势观察和实用建议 + 3. 语言温暖、积极、有建设性 + 4. 使用完整的标点符号,确保内容通顺 + 5. 直接给出分析结果,不要有前言后语 + + 请直接返回分析文本内容。 + """ + + def generate(): + try: + response = client.chat.completions.create( + model="deepseek-chat", + messages=[ + {"role": "system", "content": system_prompt}, + {"role": "user", "content": f"请分析以下日记内容:\n\n{content}"} + ], + stream=True, + max_tokens=2000 + ) + + for chunk in response: + if chunk.choices[0].delta.content is not None: + yield f"data: {json.dumps({'content': chunk.choices[0].delta.content})}\n\n" + + except Exception as e: + print(f"流式生成周报分析出错: {str(e)}") + # 返回错误信息 + error_msg = "分析过程中出现错误,请稍后重试。" + yield f"data: {json.dumps({'content': error_msg})}\n\n" + + return app.response_class(generate(), mimetype='text/plain') + + except Exception as e: + print(f"初始化流式生成周报分析出错: {str(e)}") + + # 返回一个默认的错误响应 + def error_generator(): + error_msg = "分析服务暂时不可用,请稍后重试。" + yield f"data: {json.dumps({'content': error_msg})}\n\n" + + return app.response_class(error_generator(), mimetype='text/plain') + + + + +# 生成周报分析(流式版本)- 改进版 +# @app.route('/generate_weekly_report_stream', methods=['POST']) +# @login_required +# def generate_weekly_report_stream(): +# data = request.get_json() +# +# if not data or 'content' not in data: +# return jsonify({"error": "请提供日记内容"}), 400 +# +# try: +# diary_count = data.get('diary_count', 1) +# content = data['content'] +# +# # 改进的系统提示词,要求结构化和详细的分析 +# system_prompt = f""" +# 你是一个专业的心理咨询师和情绪分析专家。请根据用户提供的{diary_count}篇日记内容,生成一份详细、结构化的情绪分析周报。 +# +# 请按照以下JSON格式返回结果,确保分析和建议都详细且有逻辑性: +# {{ +# "emotional_tags": ["标签1", "标签2", "标签3", "标签4", "标签5"], +# "emotional_trend": "上升/下降/平稳/波动", +# "main_emotion": "主要情绪名称", +# "intensity_score": 数字1-10, +# "analysis_content": \"\"\" +# 【情绪状态概述】 +# 这里写总体情绪状态的概括性描述,约100字。 +# +# 【具体表现分析】 +# 这里写具体的情绪表现分析,包括积极情绪和消极情绪的具体表现,约200字。 +# +# 【情绪变化规律】 +# 这里写情绪变化的规律和模式分析,约150字。 +# +# 【潜在关注点】 +# 这里写需要关注的情绪问题或潜在风险,约100字。 +# +# 【改善建议方向】 +# 这里写总体的改善方向和建议框架,约100字。 +# \"\"\", +# "suggestions": [ +# "第一条具体可行的建议,包含具体的行动步骤和时间安排", +# "第二条具体可行的建议,包含具体的行动步骤和时间安排", +# "第三条具体可行的建议,包含具体的行动步骤和时间安排", +# "第四条具体可行的建议,包含具体的行动步骤和时间安排", +# "第五条具体可行的建议,包含具体的行动步骤和时间安排" +# ] +# }} +# +# 要求: +# 1. emotional_tags:提取5个最精准的情绪标签 +# 2. analysis_content:使用清晰的分段结构,每个部分用【】标注,总共约650字 +# 3. suggestions:提供5条具体、可行、详细的建议,每条建议都包含具体的行动指南 +# 4. 语言专业但温暖,有建设性 +# +# 请确保返回纯JSON格式。 +# """ +# +# def generate(): +# try: +# response = client.chat.completions.create( +# model="deepseek-chat", +# messages=[ +# {"role": "system", "content": system_prompt}, +# {"role": "user", "content": f"请分析以下{diary_count}篇日记内容:\n\n{content}"} +# ], +# stream=False, +# max_tokens=3000 +# ) +# +# full_response = response.choices[0].message.content.strip() +# print("AI原始响应:", full_response) +# +# # 清理响应,提取JSON部分 +# json_start = full_response.find('{') +# json_end = full_response.rfind('}') + 1 +# +# if json_start >= 0 and json_end > json_start: +# json_str = full_response[json_start:json_end] +# try: +# parsed_data = json.loads(json_str) +# +# # 先发送情绪标签和指标 +# yield f"data: {json.dumps({'type': 'emotional_metrics', 'data': { +# 'emotional_tags': parsed_data.get('emotional_tags', []), +# 'main_emotion': parsed_data.get('main_emotion', '平静'), +# 'intensity_score': parsed_data.get('intensity_score', 5), +# 'emotional_trend': parsed_data.get('emotional_trend', '平稳') +# }})}\n\n" +# +# # 流式发送分析内容 +# analysis_content = parsed_data.get('analysis_content', '') +# if analysis_content: +# for i in range(0, len(analysis_content), 5): +# chunk = analysis_content[i:i + 5] +# yield f"data: {json.dumps({'type': 'stream_content', 'content': chunk})}\n\n" +# +# # 流式发送建议内容 +# suggestions = parsed_data.get('suggestions', []) +# if suggestions: +# suggestions_text = "\n\n".join( +# [f"{i + 1}. {suggestion}" for i, suggestion in enumerate(suggestions)]) +# for i in range(0, len(suggestions_text), 5): +# chunk = suggestions_text[i:i + 5] +# yield f"data: {json.dumps({'type': 'suggestions_stream', 'content': chunk})}\n\n" +# +# # 最后发送完整数据 +# yield f"data: {json.dumps({'type': 'structured', 'data': parsed_data})}\n\n" +# +# except json.JSONDecodeError as e: +# print(f"JSON解析错误: {e}") +# #default_data = create_default_analysis(content, diary_count) +# #yield f"data: {json.dumps({'type': 'structured', 'data': default_data})}\n\n" +# +# +# +# +# except Exception as e: +# print(f"生成周报分析出错: {str(e)}") +# #default_data = create_default_analysis(content, diary_count) +# #yield f"data: {json.dumps({'type': 'structured', 'data': default_data})}\n\n" +# +# return app.response_class(generate(), mimetype='text/plain') +# +# except Exception as e: +# print(f"初始化流式生成周报分析出错: {str(e)}") + + +# 其他页面路由 +@app.route('/emotionanalyzer') +@login_required +def emotionanalyzer(): + return render_template('emotionanalyzer.html') + + +# 日记管理功能 +@app.route('/diary', methods=['GET', 'POST']) +@login_required +def diary(): + if request.method == 'POST': + # 创建或更新日记 + title = request.form['title'] + # 使用HTML内容而不是普通文本 + content = request.form.get('content-html', request.form.get('content', '')) + tags = request.form.get('tags', '') + diary_id = request.form.get('diary_id') + + # 检查内容是否为空(移除HTML标签后检查) + if not re.sub(r'<[^>]+>', '', content).strip(): + flash('日记内容不能为空。', 'error') + return redirect(url_for('diary_list')) + + conn = sqlite3.connect('database.db') + c = conn.cursor() + + if diary_id: + # 更新现有日记 + c.execute('''UPDATE diaries + SET title = ?, content = ?, tags = ?, updated_at = CURRENT_TIMESTAMP + WHERE id = ? AND user_id = ?''', + (title, content, tags, diary_id, session['user_id'])) + flash('日记已更新!', 'success') + else: + # 创建新日记 + c.execute('''INSERT INTO diaries (user_id, title, content, tags) + VALUES (?, ?, ?, ?)''', + (session['user_id'], title, content, tags)) + + flash('日记已保存!', 'success') + + conn.commit() + conn.close() + + return redirect(url_for('diary_list')) + + # GET 请求,显示日记编辑页面 + diary_id = request.args.get('id') + diary_data = None + + if diary_id: + conn = sqlite3.connect('database.db') + c = conn.cursor() + c.execute('SELECT id, title, content, tags FROM diaries WHERE id = ? AND user_id = ?', + (diary_id, session['user_id'])) + diary_data = c.fetchone() + conn.close() + + if not diary_data: + flash('您没有权限访问此日记!', 'error') + return redirect(url_for('diary_list')) + + # 添加支持编辑模式下的HTML内容显示 + return render_template('diary.html', diary=diary_data, content_as_html=True) + + +# 日记列表 +@app.route('/diary_list') +@login_required +def diary_list(): + conn = sqlite3.connect('database.db') + c = conn.cursor() + c.execute('''SELECT id, title, content, tags, created_at, updated_at + FROM diaries + WHERE user_id = ? + ORDER BY updated_at DESC''', + (session['user_id'],)) + diaries = c.fetchall() + conn.close() + + return render_template('diary_list.html', diaries=diaries) + + +# 删除日记 +@app.route('/diary_delete/') +@login_required +def diary_delete(diary_id): + conn = sqlite3.connect('database.db') + c = conn.cursor() + c.execute('DELETE FROM diaries WHERE id = ? AND user_id = ?', + (diary_id, session['user_id'])) + conn.commit() + conn.close() + + flash('日记已删除!', 'success') + return redirect(url_for('diary_list')) + + +# 日记查看功能 +@app.route('/diary_view/') +@login_required +def diary_view(diary_id): + conn = sqlite3.connect('database.db') + c = conn.cursor() + c.execute('SELECT id, title, content, tags FROM diaries WHERE id = ? AND user_id = ?', + (diary_id, session['user_id'])) + diary_data = c.fetchone() + conn.close() + + if not diary_data: + flash('您没有权限访问此日记!', 'error') + return redirect(url_for('diary_list')) + + # 直接使用HTML内容显示(如果内容已经是HTML格式) + diary_html = diary_data + + return render_template('diary.html', diary=diary_html, view_only=True, content_as_html=True) + + +# 保存情绪分析结果到数据库 +@app.route('/save_emotion_analysis', methods=['POST']) +@login_required +def save_emotion_analysis(): + data = request.get_json() + + if not data or 'diary_id' not in data or 'analysis_result' not in data: + return jsonify({"error": "缺少必要参数"}), 400 + + diary_id = data['diary_id'] + analysis_result = data['analysis_result'] + + # 验证日记属于当前用户 + conn = sqlite3.connect('database.db') + c = conn.cursor() + c.execute('SELECT id FROM diaries WHERE id = ? AND user_id = ?', + (diary_id, session['user_id'])) + diary = c.fetchone() + + if not diary: + conn.close() + return jsonify({"error": "您没有权限访问此日记"}), 403 + + # 插入新的情绪分析结果 + c.execute('''INSERT INTO emotion_analyses + (diary_id, dominant_emotion, analysis, emotion_scores, suggestion) + VALUES (?, ?, ?, ?, ?)''', + (diary_id, + analysis_result.get('dominant_emotion', '未知'), + analysis_result.get('analysis', ''), + json.dumps(analysis_result.get('emotion_scores', {})), + analysis_result.get('suggestion', ''))) + + # 检查并限制分析结果数量不超过99条 + c.execute('''SELECT COUNT(*) FROM emotion_analyses WHERE diary_id = ?''', (diary_id,)) + count = c.fetchone()[0] + + if count > 99: + # 删除最旧的记录 + c.execute('''DELETE FROM emotion_analyses + WHERE diary_id = ? + AND id IN (SELECT id FROM emotion_analyses + WHERE diary_id = ? + ORDER BY created_at ASC + LIMIT ?)''', + (diary_id, diary_id, count - 99)) + + conn.commit() + conn.close() + + return jsonify({"success": True}) + + +# 获取日记的情绪分析结果 +@app.route('/get_emotion_analysis/') +@login_required +def get_emotion_analysis(diary_id): + conn = sqlite3.connect('database.db') + c = conn.cursor() + + # 验证日记属于当前用户 + c.execute('SELECT id FROM diaries WHERE id = ? AND user_id = ?', + (diary_id, session['user_id'])) + diary = c.fetchone() + + if not diary: + conn.close() + return jsonify({"error": "您没有权限访问此日记"}), 403 + + c.execute('''SELECT dominant_emotion, analysis, emotion_scores, suggestion + FROM emotion_analyses WHERE diary_id = ?''', + (diary_id,)) + analysis = c.fetchone() + conn.close() + + if not analysis: + return jsonify({"exists": False}) + + return jsonify({ + "exists": True, + "dominant_emotion": analysis[0], + "analysis": analysis[1], + "emotion_scores": json.loads(analysis[2]), + "suggestion": analysis[3] + }) + + +# 获取用户日记列表数据(用于情绪分析页面下拉选择) +@app.route('/diary_list_data') +@login_required +def diary_list_data(): + conn = sqlite3.connect('database.db') + c = conn.cursor() + c.execute('''SELECT id, title, created_at + FROM diaries + WHERE user_id = ? + ORDER BY updated_at DESC''', + (session['user_id'],)) + diaries = c.fetchall() + conn.close() + + # 格式化日期时间 + formatted_diaries = [] + for diary in diaries: + created_at = datetime.strptime(diary[2], '%Y-%m-%d %H:%M:%S') + formatted_diaries.append({ + 'id': diary[0], + 'title': diary[1], + 'created_at': created_at.strftime('%Y-%m-%d') + }) + + return jsonify(formatted_diaries) + + +# 获取单篇日记内容(用于情绪分析页面加载) +@app.route('/get_diary/') +@login_required +def get_diary(diary_id): + conn = sqlite3.connect('database.db') + c = conn.cursor() + + # 验证日记属于当前用户 + c.execute('SELECT id, title, content FROM diaries WHERE id = ? AND user_id = ?', + (diary_id, session['user_id'])) + diary = c.fetchone() + conn.close() + + if not diary: + return jsonify({"error": "您没有权限访问此日记"}), 403 + + return jsonify({ + 'id': diary[0], + 'title': diary[1], + 'content': diary[2] + }) + + +# 插入的卡片 +@app.route('/get_user_cards') +@login_required +def get_user_cards(): + try: + user_id = session['user_id'] + conn = sqlite3.connect('database.db') + c = conn.cursor() + + c.execute(""" + SELECT card_name, card_type, card_image_url + FROM collected_cards + WHERE user_id = ? + ORDER BY collected_at DESC + """, (user_id,)) + + cards = c.fetchall() + conn.close() + + # 转换为前端需要的格式 + user_cards = [] + for card in cards: + user_cards.append({ + "name": card[0], # card_name + "directory": card[1], # card_type + "imageUrl": card[2] # card_image_url + }) + + return jsonify(user_cards) + + except Exception as e: + print(f"获取用户卡片失败: {str(e)}") + return jsonify([]) + + +# 卡册页面 +@app.route('/chuka') +@login_required +def chuka(): + return render_template('chuka.html') + + +# 抽卡结果展示 +@app.route('/showcard') +@login_required +def showcard(): + return render_template('showcard.html') + + +# 获取所有卡片 +def get_all_cards(): + try: + imgs_root = os.path.join(app.root_path, 'static', 'imgs') + if not os.path.exists(imgs_root): + return [] + + all_cards = [] + image_exts = ['.jpg', '.jpeg', '.png', '.gif'] # 支持的图片格式 + + # 遍历imgs下的子文件夹 + for dirpath, dirnames, filenames in os.walk(imgs_root): + # 跳过根目录,只处理子文件夹 + if dirpath == imgs_root: + continue + + # 分类名 = 子文件夹名 + category = os.path.basename(dirpath) + + # 遍历文件夹下的所有图片,生成卡片信息 + for filename in filenames: + if any(filename.lower().endswith(ext) for ext in image_exts): + card_name = os.path.splitext(filename)[0] # 卡片去扩展名 + image_url = f"/static/imgs/{category}/{filename}" # 前端可访问的图片路径 + + all_cards.append({ + "name": card_name, + "directory": category, # 分类 + "imageUrl": image_url + }) + return all_cards + except Exception as e: + print(f"获取卡片列表失败:{e}") + return [] + + +# 抽卡接口 +@app.route('/draw_card', methods=['POST']) +@login_required +def draw_card(): + user_id = session['user_id'] + + # 获取系统中所有卡片(按分类) + all_cards = get_all_cards() + if not all_cards: + return jsonify({"error": "无可用卡片", "is_full": False}), 500 + + # 查询用户已拥有的卡片 + conn = sqlite3.connect('database.db') + c = conn.cursor() + c.execute(""" + SELECT card_name, card_type + FROM collected_cards + WHERE user_id = ? + """, (user_id,)) + + owned_cards = set((row[1], row[0]) for row in c.fetchall()) + conn.close() + + # 没有的卡片 + unowned_cards = [ + card for card in all_cards + if (card['directory'], card['name']) not in owned_cards + ] + if not unowned_cards: + # 已收集所有卡片 + return jsonify({"error": "你已经收集所有卡片啦!", "is_full": True}), 200 + + # 随机抽取一张未拥有的卡片 + drawn_card = random.choice(unowned_cards) + + # 存入 + conn = sqlite3.connect('database.db') + c = conn.cursor() + c.execute(""" + INSERT INTO collected_cards + (user_id, card_name, card_type, card_content, card_image_url) + VALUES (?, ?, ?, ?, ?) + """, ( + user_id, + drawn_card['name'], # 卡片名 + drawn_card['directory'], # 分类 + f"获得{drawn_card['directory']}系列卡片:{drawn_card['name']}", # 卡片描述 + drawn_card['imageUrl'] # 图片路径 + )) + conn.commit() + conn.close() + + # 返回抽中结果 + return jsonify({ + "success": True, + "drawn_card": drawn_card # 包含name、directory、imageUrl + }) + + +# 新增:按分类获取用户卡片 +@app.route('/get_cards_by_category') +@login_required +def get_cards_by_category(): + user_id = session['user_id'] + conn = sqlite3.connect('database.db') + c = conn.cursor() + + # 查询用户卡片,按分类(card_type=文件夹名)分组 + c.execute(""" + SELECT card_type, card_name, card_image_url, collected_at + FROM collected_cards + WHERE user_id = ? + ORDER BY card_type, collected_at DESC + """, (user_id,)) + cards = c.fetchall() + conn.close() + + # 按分类整理数据:{分类名: [卡片列表]} + category_cards = {} + for card in cards: + category = card[0] # 分类名(文件夹名) + if category not in category_cards: + category_cards[category] = [] + + # 格式化卡片信息(去掉稀有度) + collected_at = datetime.strptime(card[3], '%Y-%m-%d %H:%M:%S') + category_cards[category].append({ + "name": card[1], + "imageUrl": card[2], + "collectedAt": collected_at.strftime('%Y-%m-%d') # 收集时间(可选) + }) + + return jsonify(category_cards) + + +# 初始化数据库 +init_db() + +if __name__ == '__main__': + app.run(debug=True, port=5000) \ No newline at end of file diff --git a/src/AiDiary/mypo/family.jpg b/src/AiDiary/mypo/family.jpg new file mode 100644 index 0000000..98a5ddb Binary files /dev/null and b/src/AiDiary/mypo/family.jpg differ diff --git a/src/AiDiary/mypo/greenback.jpg b/src/AiDiary/mypo/greenback.jpg new file mode 100644 index 0000000..62b75ca Binary files /dev/null and b/src/AiDiary/mypo/greenback.jpg differ diff --git a/src/AiDiary/mypo/pro.jpg b/src/AiDiary/mypo/pro.jpg new file mode 100644 index 0000000..ced4c05 Binary files /dev/null and b/src/AiDiary/mypo/pro.jpg differ diff --git a/src/AiDiary/requirements.txt b/src/AiDiary/requirements.txt new file mode 100644 index 0000000..a23e858 --- /dev/null +++ b/src/AiDiary/requirements.txt @@ -0,0 +1,5 @@ +flask==2.3.3 +flask-cors==4.0.0 +requests==2.31.0 +python-dotenv==1.0.0 +openai==1.3.7 \ No newline at end of file diff --git a/src/AiDiary/static/css/theme.css b/src/AiDiary/static/css/theme.css new file mode 100644 index 0000000..b5936a3 --- /dev/null +++ b/src/AiDiary/static/css/theme.css @@ -0,0 +1,601 @@ +:root { + /* 主题色彩方案 */ + --primary-green: #e8f5e9; /* 主淡绿色 */ + --secondary-green: #c8e6c9; /* 次淡绿色 */ + --accent-green: #a5d6a7; /* 强调淡绿色 */ + --light-green: #f1f8e9; /* 浅色淡绿色 */ + --dark-green: #81c784; /* 深色淡绿色 */ + --text-color: #4a4a4a; /* 文本颜色 */ + --heading-color: #333333; /* 标题颜色 */ + --border-radius: 16px; /* 圆角增强 */ + --box-shadow: 0 6px 24px rgba(0,0,0,0.08); /* 阴影增强 */ + --transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); /* 过渡动画 */ + + /* 情绪标签颜色 */ + --color-orange-100: #ffe0cc; + --color-orange-800: #cc6633; + --color-pink-100: #ffcce0; + --color-pink-800: #cc3366; + --color-indigo-100: #ccd6ff; + --color-indigo-800: #3366cc; + --color-cyan-100: #ccf2ff; + --color-cyan-800: #33ccff; + --color-gray-100: #f0f0f0; + --color-gray-800: #666666; + + /* 活动标签颜色 */ + --color-green-200: #e0ffcc; + --color-green-800: #66cc33; + --color-blue-200: #cce0ff; + --color-blue-800: #3366cc; + --color-red-200: #ffcce0; + --color-red-800: #cc3366; + --color-purple-200: #e6ccff; + --color-purple-800: #9933cc; + --color-yellow-200: #fff2cc; + --color-yellow-800: #cc9933; + --color-orange-200: #ffecd9; + --color-orange-800: #ff9933; + --color-pink-200: #ffd9ec; + --color-pink-800: #ff3399; + + /* 场景标签颜色 */ + --color-gray-200: #f5f5f5; + --color-yellow-300: #fff5cc; + --color-orange-300: #ffe6cc; + --color-blue-300: #cce6ff; + --color-green-300: #d9ffcc; + --color-purple-300: #e6d9ff; +} + +/* 全局样式重置 */ +body { + background-image: url('/mypo/greenback.jpg'); + background-size: cover; + background-position: center bottom; + background-repeat: no-repeat; + background-attachment: fixed; + color: var(--text-color); + font-family: 'Segoe UI', 'Microsoft YaHei', sans-serif; + line-height: 1.7; + min-height: 100vh; + margin: 0; + padding: 0; + overflow-x: hidden; /* 防止水平滚动条 */ +} + +html { + min-height: 100%; + overflow-x: hidden; /* 防止水平滚动条 */ +} + +/* 标签样式 */ +.tag-item { + cursor: pointer; + transition: var(--transition); + font-weight: 500; +} + +.tag-item:hover { + transform: scale(1.05); + box-shadow: var(--box-shadow); +} + +/* 情绪标签颜色样式 */ +.bg-orange-100 { background-color: var(--color-orange-100); } +.text-orange-800 { color: var(--color-orange-800); } +.bg-pink-100 { background-color: var(--color-pink-100); } +.text-pink-800 { color: var(--color-pink-800); } +.bg-indigo-100 { background-color: var(--color-indigo-100); } +.text-indigo-800 { color: var(--color-indigo-800); } +.bg-cyan-100 { background-color: var(--color-cyan-100); } +.text-cyan-800 { color: var(--color-cyan-800); } +.bg-gray-100 { background-color: var(--color-gray-100); } +.text-gray-800 { color: var(--color-gray-800); } + +/* 活动标签颜色样式 */ +.bg-green-200 { background-color: var(--color-green-200); } +.text-green-800 { color: var(--color-green-800); } +.bg-blue-200 { background-color: var(--color-blue-200); } +.text-blue-800 { color: var(--color-blue-800); } +.bg-red-200 { background-color: var(--color-red-200); } +.text-red-800 { color: var(--color-red-800); } +.bg-purple-200 { background-color: var(--color-purple-200); } +.text-purple-800 { color: var(--color-purple-800); } +.bg-yellow-200 { background-color: var(--color-yellow-200); } +.text-yellow-800 { color: var(--color-yellow-800); } +.bg-orange-200 { background-color: var(--color-orange-200); } +.text-orange-800 { color: var(--color-orange-800); } +.bg-pink-200 { background-color: var(--color-pink-200); } +.text-pink-800 { color: var(--color-pink-800); } + +/* 场景标签颜色样式 */ +.bg-gray-200 { background-color: var(--color-gray-200); } +.bg-yellow-300 { background-color: var(--color-yellow-300); } +.bg-orange-300 { background-color: var(--color-orange-300); } +.bg-blue-300 { background-color: var(--color-blue-300); } +.bg-green-300 { background-color: var(--color-green-300); } +.bg-purple-300 { background-color: var(--color-purple-300); } + +/* 导航栏样式 */ +.navbar { + background: linear-gradient(135deg, var(--primary-green) 0%, var(--secondary-green) 100%) !important; + box-shadow: var(--box-shadow); + padding: 0.8rem 0; +} + +.navbar-brand { + color: var(--heading-color) !important; + font-weight: 800; + font-size: 1.6rem; + text-shadow: 0 2px 4px rgba(255,255,255,0.3); + transition: var(--transition); +} + +.navbar-brand:hover { + transform: scale(1.05); +} + +.nav-link { + color: var(--heading-color) !important; + font-weight: 600; + position: relative; + padding: 0.8rem 1rem !important; + margin: 0 0.3rem; + border-radius: var(--border-radius); + transition: var(--transition); +} + +.nav-link:hover { + background-color: rgba(255,255,255,0.2); + transform: translateY(-2px); +} + +.nav-link::after { + content: ''; + position: absolute; + width: 0; + height: 3px; + bottom: 0.5rem; + left: 50%; + background-color: var(--heading-color); + transition: var(--transition); + border-radius: 3px; +} + +.nav-link:hover::after { + width: 70%; + left: 15%; +} + +/* 下拉菜单样式 */ +.dropdown-menu { + background-color: white !important; + border: 2px solid var(--primary-green) !important; + border-radius: var(--border-radius) !important; + box-shadow: var(--box-shadow) !important; + margin-top: 0.5rem !important; + overflow: hidden; +} + +.dropdown-item { + color: var(--text-color) !important; + font-weight: 500; + padding: 0.8rem 1.5rem !important; + transition: var(--transition); + border-radius: 0 !important; +} + +.dropdown-item:hover { + background-color: var(--light-green) !important; + color: var(--heading-color) !important; + transform: translateX(5px); +} + +.dropdown-item.active { + background-color: var(--primary-green) !important; + color: var(--heading-color) !important; +} + +/* 英雄区域样式 */ +.hero-section { + background-color: rgba(255, 255, 255, 0.02); /* 设置透明度为0.02(保持整个页面蒙版透明度一致) */ + color: var(--heading-color); + min-height: 100vh; + display: flex; + align-items: center; + position: relative; + overflow: hidden; + backdrop-filter: blur(5px); +} + +.hero-text { + min-width: 0; +} + +.hero-text h1 { + text-align: left; + white-space: normal; + display: block; +} + + + +/* 卡片样式 */ +.card { + background-color: white; + border: 2px solid var(--primary-green); + border-radius: var(--border-radius); + box-shadow: var(--box-shadow); + transition: var(--transition); + overflow: hidden; +} + +.card:hover { + transform: translateY(-10px); + box-shadow: 0 15px 40px rgba(0,0,0,0.12); + border-color: var(--secondary-green); +} + +.card-header { + background: linear-gradient(135deg, var(--primary-green) 0%, var(--light-green) 100%); + color: var(--heading-color); + font-weight: 700; + padding: 1.2rem 1.5rem; + border-bottom: 2px solid var(--primary-green); +} + +.card-title { + color: var(--heading-color); + font-weight: 700; + margin-bottom: 0.8rem; +} + +/* 按钮样式 */ +.btn { + border-radius: var(--border-radius); + font-weight: 600; + padding: 0.8rem 2rem; + transition: var(--transition); + border: 2px solid transparent; + position: relative; + overflow: hidden; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.btn-primary { + background-color: var(--primary-green); + color: var(--heading-color); + border-color: var(--primary-green); +} + +.btn-primary:hover { + background-color: var(--secondary-green); + border-color: var(--secondary-green); + color: var(--heading-color); + transform: translateY(-2px); + box-shadow: 0 10px 30px rgba(165, 214, 167, 0.3); +} + +.btn-secondary { + background-color: white; + color: var(--heading-color); + border-color: var(--primary-green); +} + +.btn-secondary:hover { + background-color: var(--light-green); + border-color: var(--secondary-green); + transform: translateY(-2px); +} + +.btn::before { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient(90deg, transparent, rgba(255,255,255,0.4), transparent); + transition: var(--transition); +} + +.btn:hover::before { + left: 100%; +} + +/* 表单样式 */ +.form-control { + border-radius: var(--border-radius); + border: 2px solid #e9ecef; + padding: 1rem 1.2rem; + transition: var(--transition); + background-color: white; +} + +.form-control:focus { + border-color: var(--primary-green); + box-shadow: 0 0 0 0.3rem rgba(165, 214, 167, 0.25); + transform: translateY(-1px); +} + +.form-label { + color: var(--heading-color); + font-weight: 600; + margin-bottom: 0.5rem; +} + +/* 标题样式 */ +h1, h2, h3, h4, h5, h6 { + color: var(--heading-color); + font-weight: 700; + line-height: 1.3; +} + +h1 { + font-size: 3rem; + margin-bottom: 1.5rem; +} + +h2 { + font-size: 2.2rem; + margin-bottom: 1.2rem; +} + +h3 { + font-size: 1.8rem; + margin-bottom: 1rem; +} + +/* 段落样式 */ +p { + color: var(--text-color); + font-size: 1.1rem; + margin-bottom: 1.2rem; + line-height: 1.8; +} + +/* 警告框样式 */ +.alert { + border-radius: var(--border-radius); + padding: 1.2rem 1.5rem; + margin-bottom: 1.5rem; + box-shadow: var(--box-shadow); + border: 2px solid transparent; +} + +.alert-success { + background: linear-gradient(135deg, #d4edda 0%, #c3e6cb 100%); + color: #155724; + border-left: 5px solid #28a745; +} + +.alert-danger { + background: linear-gradient(135deg, #f8d7da 0%, #f5c6cb 100%); + color: #721c24; + border-left: 5px solid #dc3545; +} + +.alert-info { + background: linear-gradient(135deg, #d1ecf1 0%, #bee5eb 100%); + color: #0c5460; + border-left: 5px solid #17a2b8; +} + +/* 页脚样式 */ +.footer { + background: linear-gradient(135deg, var(--primary-green) 0%, var(--dark-green) 100%); + color: var(--heading-color); + padding: 4rem 0 2rem; + margin-top: 4rem; + position: relative; +} + +.footer a { + color: var(--heading-color); + text-decoration: none; + transition: var(--transition); +} + +.footer a:hover { + color: white; + transform: translateX(3px); +} + +/* 响应式设计 */ +@media (max-width: 768px) { + .navbar { + padding: 0.5rem 0; + } + + .nav-link { + padding: 0.6rem 0.8rem !important; + margin: 0.2rem 0; + } + + .hero-section { + padding: 80px 0; + } + + h1 { + font-size: 2.2rem; + } + + h2 { + font-size: 1.8rem; + } + + .btn { + padding: 0.7rem 1.5rem; + font-size: 0.9rem; + } +} + +/* 加载动画 */ +.loading { + display: inline-block; + width: 24px; + height: 24px; + border: 3px solid rgba(74, 74, 74, 0.2); + border-radius: 50%; + border-top-color: var(--heading-color); + animation: spin 1s ease-in-out infinite; +} + +@keyframes spin { + to { transform: rotate(360deg); } +} + +/* 淡入动画 */ +.fade-in { + animation: fadeIn 0.6s ease-in-out; +} + +@keyframes fadeIn { + from { + opacity: 0; + transform: translateY(20px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +/* 卡片增强效果 */ +.card-enhanced { + transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275); + position: relative; + overflow: hidden; +} + +.card-enhanced::before { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent); + transition: left 0.5s; +} + +.card-enhanced:hover::before { + left: 100%; +} + +.card-enhanced:hover { + transform: translateY(-12px) scale(1.03); + box-shadow: 0 20px 60px rgba(0,0,0,0.15); +} + +.feature-card { + padding: 1.5rem; + border-radius: var(--border-radius); +} + +/* 徽章样式 */ +.badge { + border-radius: 20px; + padding: 0.5rem 1rem; + font-weight: 600; + font-size: 0.85rem; +} + +.badge-primary { + background-color: var(--primary-green); + color: var(--heading-color); +} + +/* 列表样式 */ +.list-group-item { + border: none; + border-radius: var(--border-radius); + margin-bottom: 0.8rem; + transition: var(--transition); + padding: 1rem 1.2rem; + background-color: white; + border: 1px solid #e9ecef; +} + +.list-group-item:hover { + background-color: var(--light-green); + transform: translateX(8px); + border-color: var(--primary-green); +} + +/* 滚动条样式 */ +::-webkit-scrollbar { + width: 8px; + height: 8px; +} + +::-webkit-scrollbar-track { + background: var(--light-green); + border-radius: 4px; +} + +::-webkit-scrollbar-thumb { + background: var(--primary-green); + border-radius: 4px; +} + +::-webkit-scrollbar-thumb:hover { + background: var(--secondary-green); +} + +/* 按钮点击反馈效果 */ +.btn-feedback { + position: relative; + overflow: hidden; +} + +.btn-feedback::after { + content: ''; + position: absolute; + top: 50%; + left: 50%; + width: 0; + height: 0; + background: rgba(255, 255, 255, 0.4); + border-radius: 50%; + transform: translate(-50%, -50%); + transition: width 0.6s, height 0.6s; +} + +.btn-feedback.clicked::after { + width: 400px; + height: 400px; +} + +/* 图表样式(如果使用ECharts) */ +.chart-container { + background-color: white; + border-radius: var(--border-radius); + padding: 1.5rem; + box-shadow: var(--box-shadow); + border: 2px solid var(--primary-green); +} + +/* AI聊天窗口样式 */ +.ai-chat-window { + background-color: white; + border-radius: var(--border-radius); + box-shadow: 0 20px 60px rgba(0,0,0,0.15); + border: 2px solid var(--primary-green); +} + +.chat-header { + background: linear-gradient(135deg, var(--primary-green) 0%, var(--secondary-green) 100%); + color: var(--heading-color); +} + +.chat-body { + background: linear-gradient(to bottom, var(--light-green), var(--primary-green)); +} + +.user-message .message-content { + background: linear-gradient(135deg, var(--primary-green) 0%, var(--secondary-green) 100%); + color: var(--heading-color); +} \ No newline at end of file diff --git a/src/AiDiary/static/imgs/GreyMouse/en.gif b/src/AiDiary/static/imgs/GreyMouse/en.gif new file mode 100644 index 0000000..3fb969d Binary files /dev/null and b/src/AiDiary/static/imgs/GreyMouse/en.gif differ diff --git a/src/AiDiary/static/imgs/GreyMouse/giveme.gif b/src/AiDiary/static/imgs/GreyMouse/giveme.gif new file mode 100644 index 0000000..f9d4932 Binary files /dev/null and b/src/AiDiary/static/imgs/GreyMouse/giveme.gif differ diff --git a/src/AiDiary/static/imgs/GreyMouse/heihei.gif b/src/AiDiary/static/imgs/GreyMouse/heihei.gif new file mode 100644 index 0000000..c29a7ce Binary files /dev/null and b/src/AiDiary/static/imgs/GreyMouse/heihei.gif differ diff --git a/src/AiDiary/static/imgs/GreyMouse/no.gif b/src/AiDiary/static/imgs/GreyMouse/no.gif new file mode 100644 index 0000000..377d42b Binary files /dev/null and b/src/AiDiary/static/imgs/GreyMouse/no.gif differ diff --git a/src/AiDiary/static/imgs/GreyMouse/tired.gif b/src/AiDiary/static/imgs/GreyMouse/tired.gif new file mode 100644 index 0000000..9fe35b4 Binary files /dev/null and b/src/AiDiary/static/imgs/GreyMouse/tired.gif differ diff --git a/src/AiDiary/static/imgs/GreyMouse/ye.gif b/src/AiDiary/static/imgs/GreyMouse/ye.gif new file mode 100644 index 0000000..82ea59b Binary files /dev/null and b/src/AiDiary/static/imgs/GreyMouse/ye.gif differ diff --git a/src/AiDiary/static/imgs/GreyMouse/哦.gif b/src/AiDiary/static/imgs/GreyMouse/哦.gif new file mode 100644 index 0000000..4ba9711 Binary files /dev/null and b/src/AiDiary/static/imgs/GreyMouse/哦.gif differ diff --git a/src/AiDiary/static/imgs/GreyMouse/坚强.gif b/src/AiDiary/static/imgs/GreyMouse/坚强.gif new file mode 100644 index 0000000..9838f9b Binary files /dev/null and b/src/AiDiary/static/imgs/GreyMouse/坚强.gif differ diff --git a/src/AiDiary/static/imgs/GreyMouse/小丑.gif b/src/AiDiary/static/imgs/GreyMouse/小丑.gif new file mode 100644 index 0000000..8b469f0 Binary files /dev/null and b/src/AiDiary/static/imgs/GreyMouse/小丑.gif differ diff --git a/src/AiDiary/static/imgs/GreyMouse/!.gif b/src/AiDiary/static/imgs/GreyMouse/!.gif new file mode 100644 index 0000000..1351381 Binary files /dev/null and b/src/AiDiary/static/imgs/GreyMouse/!.gif differ diff --git a/src/AiDiary/static/imgs/littledog/bigsad.jpg b/src/AiDiary/static/imgs/littledog/bigsad.jpg new file mode 100644 index 0000000..7d24c9f Binary files /dev/null and b/src/AiDiary/static/imgs/littledog/bigsad.jpg differ diff --git a/src/AiDiary/static/imgs/littledog/dog.png b/src/AiDiary/static/imgs/littledog/dog.png new file mode 100644 index 0000000..bc32008 Binary files /dev/null and b/src/AiDiary/static/imgs/littledog/dog.png differ diff --git a/src/AiDiary/static/imgs/littledog/gogogo.png b/src/AiDiary/static/imgs/littledog/gogogo.png new file mode 100644 index 0000000..84760e2 Binary files /dev/null and b/src/AiDiary/static/imgs/littledog/gogogo.png differ diff --git a/src/AiDiary/static/imgs/littledog/good.png b/src/AiDiary/static/imgs/littledog/good.png new file mode 100644 index 0000000..a2bb39e Binary files /dev/null and b/src/AiDiary/static/imgs/littledog/good.png differ diff --git a/src/AiDiary/static/imgs/littledog/happiness.jpg b/src/AiDiary/static/imgs/littledog/happiness.jpg new file mode 100644 index 0000000..1d832ff Binary files /dev/null and b/src/AiDiary/static/imgs/littledog/happiness.jpg differ diff --git a/src/AiDiary/static/imgs/littledog/happy.jpg b/src/AiDiary/static/imgs/littledog/happy.jpg new file mode 100644 index 0000000..e907537 Binary files /dev/null and b/src/AiDiary/static/imgs/littledog/happy.jpg differ diff --git a/src/AiDiary/static/imgs/littledog/hug.png b/src/AiDiary/static/imgs/littledog/hug.png new file mode 100644 index 0000000..6f9da97 Binary files /dev/null and b/src/AiDiary/static/imgs/littledog/hug.png differ diff --git a/src/AiDiary/static/imgs/littledog/littlesad.jpg b/src/AiDiary/static/imgs/littledog/littlesad.jpg new file mode 100644 index 0000000..adff7fa Binary files /dev/null and b/src/AiDiary/static/imgs/littledog/littlesad.jpg differ diff --git a/src/AiDiary/static/imgs/littledog/loveu.png b/src/AiDiary/static/imgs/littledog/loveu.png new file mode 100644 index 0000000..e49f2f2 Binary files /dev/null and b/src/AiDiary/static/imgs/littledog/loveu.png differ diff --git a/src/AiDiary/static/imgs/littledog/oh.jpg b/src/AiDiary/static/imgs/littledog/oh.jpg new file mode 100644 index 0000000..a2e5f81 Binary files /dev/null and b/src/AiDiary/static/imgs/littledog/oh.jpg differ diff --git a/src/AiDiary/static/imgs/littledog/poor.png b/src/AiDiary/static/imgs/littledog/poor.png new file mode 100644 index 0000000..3e3e013 Binary files /dev/null and b/src/AiDiary/static/imgs/littledog/poor.png differ diff --git a/src/AiDiary/static/imgs/littledog/shit.jpg b/src/AiDiary/static/imgs/littledog/shit.jpg new file mode 100644 index 0000000..9baffe6 Binary files /dev/null and b/src/AiDiary/static/imgs/littledog/shit.jpg differ diff --git a/src/AiDiary/static/imgs/littledog/sleep.jpg b/src/AiDiary/static/imgs/littledog/sleep.jpg new file mode 100644 index 0000000..f149c3d Binary files /dev/null and b/src/AiDiary/static/imgs/littledog/sleep.jpg differ diff --git a/src/AiDiary/static/imgs/littledog/teeth.png b/src/AiDiary/static/imgs/littledog/teeth.png new file mode 100644 index 0000000..b27ce91 Binary files /dev/null and b/src/AiDiary/static/imgs/littledog/teeth.png differ diff --git a/src/AiDiary/static/imgs/littledog/wing.png b/src/AiDiary/static/imgs/littledog/wing.png new file mode 100644 index 0000000..8e0697e Binary files /dev/null and b/src/AiDiary/static/imgs/littledog/wing.png differ diff --git a/src/AiDiary/static/imgs/llrdog/angry.gif b/src/AiDiary/static/imgs/llrdog/angry.gif new file mode 100644 index 0000000..9fbdc52 Binary files /dev/null and b/src/AiDiary/static/imgs/llrdog/angry.gif differ diff --git a/src/AiDiary/static/imgs/llrdog/fly.gif b/src/AiDiary/static/imgs/llrdog/fly.gif new file mode 100644 index 0000000..6e62462 Binary files /dev/null and b/src/AiDiary/static/imgs/llrdog/fly.gif differ diff --git a/src/AiDiary/static/imgs/llrdog/hi.gif b/src/AiDiary/static/imgs/llrdog/hi.gif new file mode 100644 index 0000000..7a01a2b Binary files /dev/null and b/src/AiDiary/static/imgs/llrdog/hi.gif differ diff --git a/src/AiDiary/static/imgs/llrdog/smile.gif b/src/AiDiary/static/imgs/llrdog/smile.gif new file mode 100644 index 0000000..3902536 Binary files /dev/null and b/src/AiDiary/static/imgs/llrdog/smile.gif differ diff --git a/src/AiDiary/static/imgs/llrdog/wang.gif b/src/AiDiary/static/imgs/llrdog/wang.gif new file mode 100644 index 0000000..3b12f17 Binary files /dev/null and b/src/AiDiary/static/imgs/llrdog/wang.gif differ diff --git a/src/AiDiary/static/imgs/poordog/AreYouOK.png b/src/AiDiary/static/imgs/poordog/AreYouOK.png new file mode 100644 index 0000000..5481fb7 Binary files /dev/null and b/src/AiDiary/static/imgs/poordog/AreYouOK.png differ diff --git a/src/AiDiary/static/imgs/poordog/call120foryou.png b/src/AiDiary/static/imgs/poordog/call120foryou.png new file mode 100644 index 0000000..fa4fe01 Binary files /dev/null and b/src/AiDiary/static/imgs/poordog/call120foryou.png differ diff --git a/src/AiDiary/static/imgs/poordog/cry.png b/src/AiDiary/static/imgs/poordog/cry.png new file mode 100644 index 0000000..3e6ad9e Binary files /dev/null and b/src/AiDiary/static/imgs/poordog/cry.png differ diff --git a/src/AiDiary/static/imgs/poordog/eatphone.png b/src/AiDiary/static/imgs/poordog/eatphone.png new file mode 100644 index 0000000..cd9933e Binary files /dev/null and b/src/AiDiary/static/imgs/poordog/eatphone.png differ diff --git a/src/AiDiary/static/imgs/poordog/gogogogo.png b/src/AiDiary/static/imgs/poordog/gogogogo.png new file mode 100644 index 0000000..240cb82 Binary files /dev/null and b/src/AiDiary/static/imgs/poordog/gogogogo.png differ diff --git a/src/AiDiary/static/imgs/poordog/killyou.png b/src/AiDiary/static/imgs/poordog/killyou.png new file mode 100644 index 0000000..3bdeee6 Binary files /dev/null and b/src/AiDiary/static/imgs/poordog/killyou.png differ diff --git a/src/AiDiary/static/imgs/poordog/like.png b/src/AiDiary/static/imgs/poordog/like.png new file mode 100644 index 0000000..f4168ef Binary files /dev/null and b/src/AiDiary/static/imgs/poordog/like.png differ diff --git a/src/AiDiary/static/imgs/poordog/love.png b/src/AiDiary/static/imgs/poordog/love.png new file mode 100644 index 0000000..3de7892 Binary files /dev/null and b/src/AiDiary/static/imgs/poordog/love.png differ diff --git a/src/AiDiary/static/imgs/poordog/no!.png b/src/AiDiary/static/imgs/poordog/no!.png new file mode 100644 index 0000000..d70e648 Binary files /dev/null and b/src/AiDiary/static/imgs/poordog/no!.png differ diff --git a/src/AiDiary/static/imgs/poordog/ohno.png b/src/AiDiary/static/imgs/poordog/ohno.png new file mode 100644 index 0000000..fdd5ab5 Binary files /dev/null and b/src/AiDiary/static/imgs/poordog/ohno.png differ diff --git a/src/AiDiary/static/imgs/poordog/sleepy.png b/src/AiDiary/static/imgs/poordog/sleepy.png new file mode 100644 index 0000000..99a0725 Binary files /dev/null and b/src/AiDiary/static/imgs/poordog/sleepy.png differ diff --git a/src/AiDiary/static/imgs/poordog/tears.png b/src/AiDiary/static/imgs/poordog/tears.png new file mode 100644 index 0000000..0a3d442 Binary files /dev/null and b/src/AiDiary/static/imgs/poordog/tears.png differ diff --git a/src/AiDiary/static/imgs/poordog/touched.png b/src/AiDiary/static/imgs/poordog/touched.png new file mode 100644 index 0000000..de50a99 Binary files /dev/null and b/src/AiDiary/static/imgs/poordog/touched.png differ diff --git a/src/AiDiary/static/imgs/poordog/whiteeye.png b/src/AiDiary/static/imgs/poordog/whiteeye.png new file mode 100644 index 0000000..010ea3e Binary files /dev/null and b/src/AiDiary/static/imgs/poordog/whiteeye.png differ diff --git a/src/AiDiary/static/imgs/poordog/!.png b/src/AiDiary/static/imgs/poordog/!.png new file mode 100644 index 0000000..0788576 Binary files /dev/null and b/src/AiDiary/static/imgs/poordog/!.png differ diff --git a/src/AiDiary/static/imgs/poordog/?.png b/src/AiDiary/static/imgs/poordog/?.png new file mode 100644 index 0000000..7be826e Binary files /dev/null and b/src/AiDiary/static/imgs/poordog/?.png differ diff --git a/src/AiDiary/static/imgs/wyjbear/awake.gif b/src/AiDiary/static/imgs/wyjbear/awake.gif new file mode 100644 index 0000000..018976a Binary files /dev/null and b/src/AiDiary/static/imgs/wyjbear/awake.gif differ diff --git a/src/AiDiary/static/imgs/wyjbear/badcomputer.gif b/src/AiDiary/static/imgs/wyjbear/badcomputer.gif new file mode 100644 index 0000000..6c82c55 Binary files /dev/null and b/src/AiDiary/static/imgs/wyjbear/badcomputer.gif differ diff --git a/src/AiDiary/static/imgs/wyjbear/bye.gif b/src/AiDiary/static/imgs/wyjbear/bye.gif new file mode 100644 index 0000000..7d08954 Binary files /dev/null and b/src/AiDiary/static/imgs/wyjbear/bye.gif differ diff --git a/src/AiDiary/static/imgs/wyjbear/confused.gif b/src/AiDiary/static/imgs/wyjbear/confused.gif new file mode 100644 index 0000000..047a22d Binary files /dev/null and b/src/AiDiary/static/imgs/wyjbear/confused.gif differ diff --git a/src/AiDiary/static/imgs/wyjbear/eat.gif b/src/AiDiary/static/imgs/wyjbear/eat.gif new file mode 100644 index 0000000..d003618 Binary files /dev/null and b/src/AiDiary/static/imgs/wyjbear/eat.gif differ diff --git a/src/AiDiary/static/imgs/wyjbear/godie.gif b/src/AiDiary/static/imgs/wyjbear/godie.gif new file mode 100644 index 0000000..1d2f928 Binary files /dev/null and b/src/AiDiary/static/imgs/wyjbear/godie.gif differ diff --git a/src/AiDiary/static/imgs/wyjbear/gohome.gif b/src/AiDiary/static/imgs/wyjbear/gohome.gif new file mode 100644 index 0000000..a40c461 Binary files /dev/null and b/src/AiDiary/static/imgs/wyjbear/gohome.gif differ diff --git a/src/AiDiary/static/imgs/wyjbear/grievanced.gif b/src/AiDiary/static/imgs/wyjbear/grievanced.gif new file mode 100644 index 0000000..8da3843 Binary files /dev/null and b/src/AiDiary/static/imgs/wyjbear/grievanced.gif differ diff --git a/src/AiDiary/static/imgs/wyjbear/hello.gif b/src/AiDiary/static/imgs/wyjbear/hello.gif new file mode 100644 index 0000000..c139469 Binary files /dev/null and b/src/AiDiary/static/imgs/wyjbear/hello.gif differ diff --git a/src/AiDiary/static/imgs/wyjbear/hi.gif b/src/AiDiary/static/imgs/wyjbear/hi.gif new file mode 100644 index 0000000..904a693 Binary files /dev/null and b/src/AiDiary/static/imgs/wyjbear/hi.gif differ diff --git a/src/AiDiary/static/imgs/wyjbear/ohno.gif b/src/AiDiary/static/imgs/wyjbear/ohno.gif new file mode 100644 index 0000000..0d25e07 Binary files /dev/null and b/src/AiDiary/static/imgs/wyjbear/ohno.gif differ diff --git a/src/AiDiary/static/imgs/wyjbear/piano.gif b/src/AiDiary/static/imgs/wyjbear/piano.gif new file mode 100644 index 0000000..4960d23 Binary files /dev/null and b/src/AiDiary/static/imgs/wyjbear/piano.gif differ diff --git a/src/AiDiary/static/imgs/wyjbear/scratchhead.gif b/src/AiDiary/static/imgs/wyjbear/scratchhead.gif new file mode 100644 index 0000000..b091821 Binary files /dev/null and b/src/AiDiary/static/imgs/wyjbear/scratchhead.gif differ diff --git a/src/AiDiary/static/imgs/wyjbear/sleep.gif b/src/AiDiary/static/imgs/wyjbear/sleep.gif new file mode 100644 index 0000000..d0aa005 Binary files /dev/null and b/src/AiDiary/static/imgs/wyjbear/sleep.gif differ diff --git a/src/AiDiary/static/imgs/wyjbear/smug.gif b/src/AiDiary/static/imgs/wyjbear/smug.gif new file mode 100644 index 0000000..38af171 Binary files /dev/null and b/src/AiDiary/static/imgs/wyjbear/smug.gif differ diff --git a/src/AiDiary/static/imgs/wyjbear/snack.gif b/src/AiDiary/static/imgs/wyjbear/snack.gif new file mode 100644 index 0000000..dcdafdd Binary files /dev/null and b/src/AiDiary/static/imgs/wyjbear/snack.gif differ diff --git a/src/AiDiary/static/imgs/wyjbear/you.gif b/src/AiDiary/static/imgs/wyjbear/you.gif new file mode 100644 index 0000000..a476540 Binary files /dev/null and b/src/AiDiary/static/imgs/wyjbear/you.gif differ diff --git a/src/AiDiary/static/useful/firecracker.gif b/src/AiDiary/static/useful/firecracker.gif new file mode 100644 index 0000000..e2bffa2 Binary files /dev/null and b/src/AiDiary/static/useful/firecracker.gif differ diff --git a/src/AiDiary/templates/base.html b/src/AiDiary/templates/base.html new file mode 100644 index 0000000..8c81a63 --- /dev/null +++ b/src/AiDiary/templates/base.html @@ -0,0 +1,802 @@ + + + + + + {% block title %}AI日记{% endblock %} + + + + + + + + + + + + + + + + {% block extra_css %}{% endblock %} + + +
+ + + + + {% with messages = get_flashed_messages(with_categories=true) %} + {% if messages %} +
+ {% for category, message in messages %} + + {% endfor %} +
+ {% endif %} + {% endwith %} + + +
+ {% block content %}{% endblock %} +
+ + + + + + {% block extra_js %}{% endblock %} + + \ No newline at end of file diff --git a/src/AiDiary/templates/chuka.html b/src/AiDiary/templates/chuka.html new file mode 100644 index 0000000..9761a32 --- /dev/null +++ b/src/AiDiary/templates/chuka.html @@ -0,0 +1,169 @@ +{% extends "base.html" %} +{% block title %}我的卡册 - AI日记{% endblock %} +{% block extra_css %} + +{% endblock %} + +{% block content %} +
+
+

我的卡册

+ +
+ +
+

加载中...

+
+
+{% endblock %} + +{% block extra_js %} + +{% endblock %} \ No newline at end of file diff --git a/src/AiDiary/templates/diary.html b/src/AiDiary/templates/diary.html new file mode 100644 index 0000000..cac656c --- /dev/null +++ b/src/AiDiary/templates/diary.html @@ -0,0 +1,435 @@ +{% extends "base.html" %} + +{% block title %}{% if view_only %}查看日记 - AI日记{% else %}创作日记 - AI日记{% endif %}{% endblock %} + +{% block extra_css %} + +{% endblock %} + +{% block content %} +
+
+
+
+
+

+ {% if view_only %} + 查看日记 + {% else %} + 记录今日心情 + {% endif %} +

+
+
+
+ {% if diary %} + + {% endif %} + +
+ + +
+ +
+
+ + {% if not view_only %} + + {% endif %} +
+ {% if view_only %} +
+ {{ diary[2] | safe }} +
+ {% else %} +
+ {% if diary %}{{ diary[2] | safe }}{% else %} +

今天发生了什么?心情如何?

+ {% endif %} +
+ + {% endif %} +
+ + + + {% if not view_only %} +
+ +
+ +
+
+ + 开心 + 难过 + 平静 + 感恩 + 焦虑 + 生气 + 兴奋 + 思考 + 好奇 + 疲惫 + + + 工作 + 学习 + 运动 + 娱乐 + 阅读 + 旅行 + 美食 + 社交 + + + 早晨 + 中午 + 晚上 + 居家 + 户外 + 特殊 + + + + 自定义 +
+
+ {% endif %} + +
+ + 返回列表 + + {% if not view_only %} + + {% endif %} +
+
+
+
+ + + + +
+
+
+{% endblock %} + +{% block extra_js %} + +{% endblock %} \ No newline at end of file diff --git a/src/AiDiary/templates/diary_list.html b/src/AiDiary/templates/diary_list.html new file mode 100644 index 0000000..635ce5f --- /dev/null +++ b/src/AiDiary/templates/diary_list.html @@ -0,0 +1,69 @@ +{% extends "base.html" %} + +{% block title %}我的日记 - AI日记{% endblock %} + +{% block content %} +
+
+
+
+
+

我的日记

+ + 写新日记 + +
+
+ {% if diaries %} +
+ {% for diary in diaries %} +
+
+

{{ diary[1] }}

+
+ + {% if diary[5] != diary[4] %} + 最后编辑: {{ diary[5].split(' ')[0] }} + {% else %} + 创建于: {{ diary[4].split(' ')[0] }} + {% endif %} + + +
+
+ {% if diary[3] %} +
+ {% for tag in diary[3].split(',') %} + {{ tag.strip() }} + {% endfor %} +
+ {% endif %} +
+ {% endfor %} +
+ {% else %} +
+ +

还没有日记呢

+

点击上方按钮开始记录你的第一篇日记吧

+ + 开始写日记 + +
+ {% endif %} +
+
+
+
+
+{% endblock %} \ No newline at end of file diff --git a/src/AiDiary/templates/emotionanalyzer.html b/src/AiDiary/templates/emotionanalyzer.html new file mode 100644 index 0000000..09980d7 --- /dev/null +++ b/src/AiDiary/templates/emotionanalyzer.html @@ -0,0 +1,987 @@ +{% extends "base.html" %} + +{% block title %}AI周报生成器 - AI日记{% endblock %} + +{% block extra_css %} + +{% endblock %} + +{% block content %} +
+
+ +
+

+ AI周报生成器 +

+

生成情感分析报告,或查看历史存档报告

+
+ + +
+
+

+ 生成新报告 +

+ +
+ +
+

+ 选择日记(最多选择7篇) +

+ +
+ 0/7 篇已选择 +
+ +
+ +
+ +

加载中...

+
+
+ + +
+
+ + +
+ + +
+ + + + + +
+
+

详细情绪分析报告

+

+
+ + +
+
+ +
+
+ 0 字 +
+
+ + +
+

存档此报告

+

将当前报告保存到您的个人档案中,方便日后查看

+
+ + +
+
+ + +
+
+ +
+ + +
+
+ + +
+
+

+ 历史报告存档 +

+ +
+ +
+
+ +
+ +

加载中...

+
+
+
+
+
+
+{% endblock %} + +{% block extra_js %} + +{% endblock %} \ No newline at end of file diff --git a/src/AiDiary/templates/index.html b/src/AiDiary/templates/index.html new file mode 100644 index 0000000..401cfcc --- /dev/null +++ b/src/AiDiary/templates/index.html @@ -0,0 +1,167 @@ +{% extends "base.html" %} + +{% block title %}首页 - AI日记{% endblock %} + +{% block extra_css %} + +{% endblock %} + +{% block content %} + +
+
+ + +
+
+
+
+

{% if session.username %}Hi,{{ session.username }}{% else %}记录生活,分析情绪{% endif %}

+

帮助你捕捉每一天的情绪变化,生成专属周报,让自我认知更清晰

+ +
+
+
+
+{% endblock %} \ No newline at end of file diff --git a/src/AiDiary/templates/login.html b/src/AiDiary/templates/login.html new file mode 100644 index 0000000..e18b445 --- /dev/null +++ b/src/AiDiary/templates/login.html @@ -0,0 +1,86 @@ +{% extends "base.html" %} + +{% block title %}用户登录{% endblock %} + +{% block extra_css %} + +{% endblock %} + +{% block content %} + +
+
+
+
+
+
+
+
+ +

用户登录

+
+ +
+
+ +
+ + +
+
+ +
+ +
+ + +
+
+ +
+ +
+
+ +
+

还没有账户? 立即注册

+
+
+
+
+
+
+{% endblock %} \ No newline at end of file diff --git a/src/AiDiary/templates/profile.html b/src/AiDiary/templates/profile.html new file mode 100644 index 0000000..834eb10 --- /dev/null +++ b/src/AiDiary/templates/profile.html @@ -0,0 +1,103 @@ +{% extends "base.html" %} + +{% block title %}个人中心{% endblock %} + +{% block content %} +
+
+
+
+
+ +
{{ session.username }}
+

AI周报用户

+
+
+ + +
+ +
+
+ +
+
+
+
个人信息
+
+
+
+
+
用户名
+
+
+ {{ session.username }} +
+
+
+
+
+
注册时间
+
+
+ {{ created_at }} +
+
+
+
+
+
+ + +
+
+
+
修改密码
+
+
+
+
+ + +
+
+ + +
+
+ + +
+ +
+
+
+
+
+
+
+
+ + +{% endblock %} \ No newline at end of file diff --git a/src/AiDiary/templates/register.html b/src/AiDiary/templates/register.html new file mode 100644 index 0000000..1ad2ac1 --- /dev/null +++ b/src/AiDiary/templates/register.html @@ -0,0 +1,87 @@ +{% extends "base.html" %} + +{% block title %}用户注册{% endblock %} + +{% block extra_css %} + +{% endblock %} + +{% block content %} + +
+
+
+
+
+
+
+
+ +

用户注册

+

创建您的账户

+
+ +
+
+ +
+ + +
+
+ +
+ +
+ + +
+
+ +
+ +
+
+ +
+

已有账户? 立即登录

+
+
+
+
+
+
+{% endblock %} \ No newline at end of file diff --git a/src/AiDiary/templates/showcard.html b/src/AiDiary/templates/showcard.html new file mode 100644 index 0000000..c9087e0 --- /dev/null +++ b/src/AiDiary/templates/showcard.html @@ -0,0 +1,151 @@ +{% extends "base.html" %} + +{% block title %}用户注册{% endblock %} + +{% block extra_css %} + +{% endblock %} + +{% block content %} +
+

!!坚持写日记三天啦!!

+ + +
正在抽取卡片...
+ + + + + +
+
+

+
+
+ 抽中卡片 +
+ +
+
+{% endblock %} + +{% block extra_js %} + +{% endblock %} \ No newline at end of file diff --git a/src/AiDiary/update_db.py b/src/AiDiary/update_db.py new file mode 100644 index 0000000..3a42c9b --- /dev/null +++ b/src/AiDiary/update_db.py @@ -0,0 +1,73 @@ +import sqlite3 + +# 连接到数据库 +conn = sqlite3.connect('database.db') +c = conn.cursor() + +# 1. 创建新的users表(不包含email字段) +c.execute('''CREATE TABLE IF NOT EXISTS new_users + (id INTEGER PRIMARY KEY AUTOINCREMENT, + username TEXT UNIQUE NOT NULL, + password TEXT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)''') + +# 2. 将旧users表中的数据复制到新表(排除email字段) +try: + c.execute('INSERT INTO new_users (id, username, password, created_at) SELECT id, username, password, created_at FROM users') +except Exception as e: + print(f"复制数据时出错: {e}") + +# 3. 删除旧表 +try: + c.execute('DROP TABLE users') +except Exception as e: + print(f"删除旧表时出错: {e}") + +# 4. 将新表重命名为users +c.execute('ALTER TABLE new_users RENAME TO users') + +# 5. 创建日记表 +c.execute('''CREATE TABLE IF NOT EXISTS diaries + (id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER NOT NULL, + title TEXT NOT NULL, + content TEXT NOT NULL, + tags TEXT, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (user_id) REFERENCES users(id))''') + +# 6. 创建情绪分析结果表 +try: + c.execute('''CREATE TABLE IF NOT EXISTS emotion_analyses + (id INTEGER PRIMARY KEY AUTOINCREMENT, + diary_id INTEGER NOT NULL, + dominant_emotion TEXT NOT NULL, + analysis TEXT NOT NULL, + emotion_scores TEXT NOT NULL, + suggestion TEXT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (diary_id) REFERENCES diaries(id) ON DELETE CASCADE)''') +except Exception as e: + print(f"创建情绪分析表时出错: {e}") + +# 7. 创建收藏卡片表 +try: + c.execute('''CREATE TABLE IF NOT EXISTS collected_cards + (id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER NOT NULL, + card_name TEXT NOT NULL, + card_type TEXT NOT NULL, + card_content TEXT NOT NULL, + card_image_url TEXT NOT NULL, + rarity TEXT NOT NULL, + collected_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (user_id) REFERENCES users(id))''') +except Exception as e: + print(f"创建收藏卡片表时出错: {e}") + +# 提交更改并关闭连接 +conn.commit() +conn.close() + +print("数据库更新完成!") \ No newline at end of file diff --git a/src/readme.txt b/src/readme.txt new file mode 100644 index 0000000..10b9632 --- /dev/null +++ b/src/readme.txt @@ -0,0 +1,6 @@ +系统简介 +该系统是一个集成日记管理、情绪分析、周报生成及卡片收集功能的AI日记应用,主要功能有: +用户注册、登录、个人信息管理、日记创建、编辑、查看、删除、基于AI的日记情绪分析、情绪分析报告存档与管理、卡片收集与展示 + +配置环境 +后端使用Python+Flask框架;前端采用HTML、CSS、JavaScript相结合;数据库由SQLite构造;引入DeepSeek对话API实现情绪分析 \ No newline at end of file