From fee4c339a26a6552bb0022022f6d65517e4ecbc9 Mon Sep 17 00:00:00 2001 From: rjb <263303411@qq.com> Date: Fri, 29 Aug 2025 22:39:03 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=E5=90=8E=E5=8F=B0=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E7=B3=BB=E7=BB=9F=E7=AC=AC=E4=B8=80=E9=98=B6=E6=AE=B5?= =?UTF-8?q?=E5=BC=80=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/__pycache__/local.cpython-312.pyc | Bin 1280 -> 1280 bytes create_admin.py | 42 ++++ migrations/__pycache__/env.cpython-312.pyc | Bin 4485 -> 4485 bytes .../89df165acd11_add_admin_user_table.py | 41 ++++ ...acd11_add_admin_user_table.cpython-312.pyc | Bin 0 -> 2371 bytes ...b6c8ec_add_favorites_table.cpython-312.pyc | Bin 55957 -> 55957 bytes src/flask_prompt_master/__init__.py | 4 + .../__pycache__/__init__.cpython-312.pyc | Bin 2351 -> 2447 bytes src/flask_prompt_master/admin/__init__.py | 123 ++++++++++ .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 6472 bytes .../admin/forms/__init__.py | 6 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 328 bytes .../__pycache__/admin_forms.cpython-312.pyc | Bin 0 -> 2399 bytes .../admin/forms/admin_forms.py | 45 ++++ .../admin/models/__init__.py | 6 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 289 bytes .../__pycache__/admin_user.cpython-312.pyc | Bin 0 -> 4025 bytes .../admin/models/admin_user.py | 58 +++++ .../admin/views/__init__.py | 14 ++ .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 474 bytes .../__pycache__/prompt_admin.cpython-312.pyc | Bin 0 -> 1803 bytes .../__pycache__/system_admin.cpython-312.pyc | Bin 0 -> 4019 bytes .../template_admin.cpython-312.pyc | Bin 0 -> 2039 bytes .../__pycache__/user_admin.cpython-312.pyc | Bin 0 -> 2214 bytes .../admin/views/prompt_admin.py | 60 +++++ .../admin/views/system_admin.py | 83 +++++++ .../admin/views/template_admin.py | 63 +++++ .../admin/views/user_admin.py | 64 +++++ .../__pycache__/__init__.cpython-312.pyc | Bin 361 -> 361 bytes .../__pycache__/favorites.cpython-312.pyc | Bin 3185 -> 3185 bytes .../models/__pycache__/models.cpython-312.pyc | Bin 7666 -> 7666 bytes .../routes/__pycache__/auth.cpython-312.pyc | Bin 9753 -> 9753 bytes .../__pycache__/favorites.cpython-312.pyc | Bin 5131 -> 5131 bytes .../routes/__pycache__/routes.cpython-312.pyc | Bin 46355 -> 46355 bytes .../__pycache__/auth_service.cpython-312.pyc | Bin 10633 -> 10633 bytes .../favorite_service.cpython-312.pyc | Bin 8801 -> 8801 bytes .../templates/admin/index.html | 116 +++++++++ .../templates/admin/login.html | 137 +++++++++++ .../templates/admin/system_dashboard.html | 223 ++++++++++++++++++ .../templates/admin/system_logs.html | 86 +++++++ 40 files changed, 1171 insertions(+) create mode 100644 create_admin.py create mode 100644 migrations/versions/89df165acd11_add_admin_user_table.py create mode 100644 migrations/versions/__pycache__/89df165acd11_add_admin_user_table.cpython-312.pyc create mode 100644 src/flask_prompt_master/admin/__init__.py create mode 100644 src/flask_prompt_master/admin/__pycache__/__init__.cpython-312.pyc create mode 100644 src/flask_prompt_master/admin/forms/__init__.py create mode 100644 src/flask_prompt_master/admin/forms/__pycache__/__init__.cpython-312.pyc create mode 100644 src/flask_prompt_master/admin/forms/__pycache__/admin_forms.cpython-312.pyc create mode 100644 src/flask_prompt_master/admin/forms/admin_forms.py create mode 100644 src/flask_prompt_master/admin/models/__init__.py create mode 100644 src/flask_prompt_master/admin/models/__pycache__/__init__.cpython-312.pyc create mode 100644 src/flask_prompt_master/admin/models/__pycache__/admin_user.cpython-312.pyc create mode 100644 src/flask_prompt_master/admin/models/admin_user.py create mode 100644 src/flask_prompt_master/admin/views/__init__.py create mode 100644 src/flask_prompt_master/admin/views/__pycache__/__init__.cpython-312.pyc create mode 100644 src/flask_prompt_master/admin/views/__pycache__/prompt_admin.cpython-312.pyc create mode 100644 src/flask_prompt_master/admin/views/__pycache__/system_admin.cpython-312.pyc create mode 100644 src/flask_prompt_master/admin/views/__pycache__/template_admin.cpython-312.pyc create mode 100644 src/flask_prompt_master/admin/views/__pycache__/user_admin.cpython-312.pyc create mode 100644 src/flask_prompt_master/admin/views/prompt_admin.py create mode 100644 src/flask_prompt_master/admin/views/system_admin.py create mode 100644 src/flask_prompt_master/admin/views/template_admin.py create mode 100644 src/flask_prompt_master/admin/views/user_admin.py create mode 100644 src/flask_prompt_master/templates/admin/index.html create mode 100644 src/flask_prompt_master/templates/admin/login.html create mode 100644 src/flask_prompt_master/templates/admin/system_dashboard.html create mode 100644 src/flask_prompt_master/templates/admin/system_logs.html diff --git a/config/__pycache__/local.cpython-312.pyc b/config/__pycache__/local.cpython-312.pyc index 85117a603e90169bab084445b21cb5f71b9a6143..ec7e862ebe5eab8dd61dd761511705211f5582da 100644 GIT binary patch delta 20 acmZqRYT)8N&CAQh00brTHg4qp$^rl~5Cw+- delta 20 acmZqRYT)8N&CAQh00fU)H*Dno$^rm1eg)hB diff --git a/create_admin.py b/create_admin.py new file mode 100644 index 0000000..bd42bd5 --- /dev/null +++ b/create_admin.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +创建管理员账户脚本 +""" +from src.flask_prompt_master import create_app, db +from src.flask_prompt_master.admin.models.admin_user import AdminUser + +def create_admin_user(): + """创建管理员用户""" + app = create_app() + + with app.app_context(): + try: + # 检查是否已存在管理员 + admin = AdminUser.query.filter_by(username='admin').first() + if admin: + print("✅ 管理员账户已存在") + return + + # 创建管理员账户 + admin = AdminUser( + username='admin', + password='admin123', + email='admin@example.com', + role='super_admin' + ) + + db.session.add(admin) + db.session.commit() + + print("✅ 管理员账户创建成功") + print("用户名: admin") + print("密码: admin123") + print("请及时修改默认密码!") + + except Exception as e: + print(f"❌ 创建管理员账户失败: {str(e)}") + db.session.rollback() + +if __name__ == '__main__': + create_admin_user() diff --git a/migrations/__pycache__/env.cpython-312.pyc b/migrations/__pycache__/env.cpython-312.pyc index 04473737e5d0188c7172eff60dea036d03239024..75d85dc4cda48cda8bb32516da71529c7cd1e270 100644 GIT binary patch delta 20 acmZowZdK+!&CAQh00brTHg4pu5(EG>X$4gP delta 20 acmZowZdK+!&CAQh00iFM8#Z!R2?78!(*+m+ diff --git a/migrations/versions/89df165acd11_add_admin_user_table.py b/migrations/versions/89df165acd11_add_admin_user_table.py new file mode 100644 index 0000000..81bb425 --- /dev/null +++ b/migrations/versions/89df165acd11_add_admin_user_table.py @@ -0,0 +1,41 @@ +"""Add admin user table + +Revision ID: 89df165acd11 +Revises: e1ec7bb6c8ec +Create Date: 2025-08-29 21:47:36.122009 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '89df165acd11' +down_revision = 'e1ec7bb6c8ec' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('admin_user', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('username', sa.String(length=50), nullable=False, comment='用户名'), + sa.Column('password_hash', sa.String(length=128), nullable=False, comment='密码哈希'), + sa.Column('email', sa.String(length=100), nullable=False, comment='邮箱'), + sa.Column('role', sa.String(length=20), nullable=False, comment='角色'), + sa.Column('is_active', sa.Boolean(), nullable=True, comment='是否激活'), + sa.Column('last_login', sa.DateTime(), nullable=True, comment='最后登录时间'), + sa.Column('created_at', sa.DateTime(), nullable=True, comment='创建时间'), + sa.Column('updated_at', sa.DateTime(), nullable=True, comment='更新时间'), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('email'), + sa.UniqueConstraint('username') + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('admin_user') + # ### end Alembic commands ### diff --git a/migrations/versions/__pycache__/89df165acd11_add_admin_user_table.cpython-312.pyc b/migrations/versions/__pycache__/89df165acd11_add_admin_user_table.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bb469e1954bb50a70c30811120526477ee36d322 GIT binary patch literal 2371 zcma);UrZcD9LIO}_WrPN6k1vY)L#Fj=1j|RXQ6OuOr*q=Qouh{lS|BIZ)eIa?w{-I z9`$@Wj8RO~fJF}i34O8{NTG%(kr;Vw;)@Mu+Kj&G9lf_I>5EU!?y=lrh}BIJH~b=F|rh(ViceOBhv~KV`$TM$*vd+*q9r* zV;H+dt*wjWisrb3G-kBq0IqS8agCEG?#$1s>PhJ$d3%ABS<0Yoal~E*yMxq#HppU? z(ul{l$~9mdy06@CTZ=mea4oTm=37Z}cvL(&o zw;z{t==*W2_i$?}=K2rd*6!gRs+e1M0JnY*_i)ABBMxqjy{hGUcgGsQ(TbVJ9L(A> z%eEZM#)_HGRLpFunAu!0vt?gqojs#+4H)}0I@pKHx@`e}h0nR|=NA1XUz$o&!Dk0B zXGIp?j4YUuz^mlq`_z;yn?VNq%x+asU@=0xh~$x>I&36TRiwE!wNM!UK0onUkU?e} zdqquAplV=$Qqc9A8UlPm&=bWB5mV-&$S;4ib$cv-YdpX9DRx6ekmMpTMe}AAKe)Sf z_a0`ECc|Qb-TU~?7mq*r7FS6+FNlUT3WqFGVR|_~@pa*+Oks5$bF!ctysQmNYRX)r z!dxal@%h%wdj7{R3)%0A*;UMmR`dhBV3bBOKAT_v!4^|VK%(NVSeRWce6>;%gB&sg z46Z2wcq{l~Mi;Q>k|w7V6?=PC0}hkb^So&wNgc-CZjDSqP_fU9wL_8ual>m!QUsLl zgXv3}svAg<$THPkQ>7azSnm0T+9vO!{uu}*GzEqbR7WI1jcXx6GITMdNW(}lNXXYi zqY#;~Uk^FMGcN!#2lGQ=ZD(n$t;sa@rjo=HKy;eWkC3cCM`B%u`jhp1JUBKuarJj% z^p5q8gQ@Q9mB}l&t}e9a*s$F@GIw%5I1^lm7gw%rT+Xrmw)E~?WIj3* zoxQkndZX6iBF}Zt_s#Up_OJB((*0JBecN_$2pg?A_Vxeif5^5xU|XhpHrW&8sfTRi z1GaJ6yU7O1EpC%N^|Zt^gLZC3wS|HRo}+2fW&uo`=L9%v%ZW3M1X zB`%3OeEo(j$YKI2Y1Cv=eQ4)r0sBopUv9>cpo$5eTz4qzm;*2gRiN{lYMm8ij^V#U z^j%amkdoocC`45Aqp6$G;faf;>Ax5c%|5NA=*Hiu#wR|ira9w#RQG(w|ER8k6#n`Q R_qXQ|?b$xV&;e@}{{m}D4x<17 literal 0 HcmV?d00001 diff --git a/migrations/versions/__pycache__/e1ec7bb6c8ec_add_favorites_table.cpython-312.pyc b/migrations/versions/__pycache__/e1ec7bb6c8ec_add_favorites_table.cpython-312.pyc index f57d46d38a1d83f6a3e402e34675f37eff645014..bfcc5bddd014cb6bcd0c9bb00e1ffc8152d40085 100644 GIT binary patch delta 22 ccmbQbm3it`X71Cxyj%=GP%>}hM((a#08xntcmMzZ delta 22 ccmbQbm3it`X71Cxyj%=G5Z=9EBX`#=08mE)sf%`lgVEK~U>?`G6? z6-W`J*d!s4NzB42B9k>)q!~pgyRuy6(`3BGm6?}W5}%loo0&IRinWPReDYk@yNt4v z1KA|l!JoMOy0}p%cTuu34w&%CX2A|=lROUz-oDu zg|DNy#eec;_KQxSz`Mnkl3$XVS60LhaIK~ysMcYVzU|MGOcEWuo)RhFqLoeZbofa{uBXT6tM$2MG_!_14PJx2w4yT(zueL gNDIW)01~%2Y;yBcN^?@}iVP>aaXNBnG4g|D0hfkG2><{9 diff --git a/src/flask_prompt_master/admin/__init__.py b/src/flask_prompt_master/admin/__init__.py new file mode 100644 index 0000000..45a44e8 --- /dev/null +++ b/src/flask_prompt_master/admin/__init__.py @@ -0,0 +1,123 @@ +# -*- coding: utf-8 -*- +""" +后台管理模块 +""" +from flask_admin import Admin, BaseView, expose +from flask_login import LoginManager, login_user, logout_user, login_required, current_user +from flask import redirect, url_for, request, flash, render_template +from src.flask_prompt_master import db +from src.flask_prompt_master.models.models import User, Prompt, PromptTemplate +from .models.admin_user import AdminUser +from .forms.admin_forms import AdminLoginForm +from .views.user_admin import UserAdminView +from .views.prompt_admin import PromptAdminView +from .views.template_admin import TemplateAdminView +from .views.system_admin import SystemAdminView +from datetime import datetime + +# 创建登录管理器 +login_manager = LoginManager() + +@login_manager.user_loader +def load_user(user_id): + """加载用户""" + return AdminUser.query.get(int(user_id)) + +def init_admin(app): + """初始化后台管理""" + # 初始化登录管理器 + login_manager.init_app(app) + login_manager.login_view = 'admin.login' + login_manager.login_message = '请先登录' + + # 创建Admin实例 + admin = Admin(app, name='提示词大师后台管理', template_mode='bootstrap4') + + # 注册视图 + admin.add_view(UserAdminView(User, db.session, name='用户管理', endpoint='admin_user')) + admin.add_view(PromptAdminView(Prompt, db.session, name='提示词管理', endpoint='admin_prompt')) + admin.add_view(TemplateAdminView(PromptTemplate, db.session, name='模板管理', endpoint='admin_template')) + admin.add_view(SystemAdminView(name='系统管理', endpoint='admin_system')) + + # 注册登录路由 + app.add_url_rule('/admin/login', 'admin.login', admin_login, methods=['GET', 'POST']) + app.add_url_rule('/admin/logout', 'admin.logout', admin_logout) + + return admin + +def admin_login(): + """管理员登录""" + if current_user.is_authenticated: + return redirect(url_for('admin.index')) + + form = AdminLoginForm() + if form.validate_on_submit(): + user = AdminUser.query.filter_by(username=form.username.data).first() + if user and user.check_password(form.password.data) and user.is_active: + login_user(user, remember=form.remember_me.data) + user.last_login = datetime.utcnow() + db.session.commit() + next_page = request.args.get('next') + return redirect(next_page or url_for('admin.index')) + else: + flash('用户名或密码错误', 'error') + + return render_template('admin/login.html', form=form) + +@login_required +def admin_logout(): + """管理员登出""" + logout_user() + flash('已成功登出', 'success') + return redirect(url_for('admin.login')) + +class AdminIndexView(BaseView): + """管理后台首页""" + + def is_accessible(self): + """检查访问权限""" + return current_user.is_authenticated + + @expose('/') + @login_required + def index(self): + """管理后台首页""" + # 获取统计数据 + from src.flask_prompt_master.models.models import User, Prompt, PromptTemplate + from sqlalchemy import func + from datetime import datetime, timedelta + + try: + # 用户统计 + total_users = User.query.count() + active_users = User.query.filter_by(status=1).count() + + # 提示词统计 + total_prompts = Prompt.query.count() + today_prompts = Prompt.query.filter( + Prompt.created_at >= datetime.now().date() + ).count() + + # 模板统计 + total_templates = PromptTemplate.query.count() + default_templates = PromptTemplate.query.filter_by(is_default=True).count() + + stats = { + 'total_users': total_users, + 'active_users': active_users, + 'total_prompts': total_prompts, + 'today_prompts': today_prompts, + 'total_templates': total_templates, + 'default_templates': default_templates + } + except Exception as e: + stats = { + 'total_users': 0, + 'active_users': 0, + 'total_prompts': 0, + 'today_prompts': 0, + 'total_templates': 0, + 'default_templates': 0 + } + + return self.render('admin/index.html', stats=stats) diff --git a/src/flask_prompt_master/admin/__pycache__/__init__.cpython-312.pyc b/src/flask_prompt_master/admin/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..967bc0f06722a11ca034b2fe9421c65a06223907 GIT binary patch literal 6472 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 IXxP2@KO@fchX4Qo literal 0 HcmV?d00001 diff --git a/src/flask_prompt_master/admin/forms/__init__.py b/src/flask_prompt_master/admin/forms/__init__.py new file mode 100644 index 0000000..8452024 --- /dev/null +++ b/src/flask_prompt_master/admin/forms/__init__.py @@ -0,0 +1,6 @@ +""" +后台管理表单包 +""" +from .admin_forms import AdminLoginForm, AdminUserForm + +__all__ = ['AdminLoginForm', 'AdminUserForm'] diff --git a/src/flask_prompt_master/admin/forms/__pycache__/__init__.cpython-312.pyc b/src/flask_prompt_master/admin/forms/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e3ad3dd699cd465e4bbb4063d9ade9974c24f22c GIT binary patch literal 328 zcmX@j%ge<81l!hc%xD49k3k$5V1hC}D*zeO8B!Rc7*ZHhm~t3%nWC6-nWLB)8B&;2 zSkjrISSndH*-FKM%DA3R=zH3~;rY6S&nL9KSh(V8@6@L~tz0h|fpVHmxA+`Wax?RM z^3yZ(-13WZZ}Ecpp~b01AZ`&eP*o8Nknq!Fy~Q3MpOT*(AAgHG5u_=SN)9q+*JLd)V!?B#Jr?@{lv_Y;$;2eqGbKFoW$bn_=2MR+=7z$ z+{EIN)FOSbq55D$_2c6+^D;}~LKA1QUXU;wM zJLlee?)lFBEfC-le78ogcmC-?=np!meD1RHq#Kk0gpq(SgB@{3aR?5E>dv@RaS1NP zEw~kr;89qCRXBmO=3H^F!VA3O6MTwa@GAi!pwtL84051%5q2L(*dsfJo%R|+(6X{% z<*KZq9&eaWHUNeM&JWU(~hbH+e805sv4K2ggx@@m*a9&FC*LJZhbqEC2I(A{5vFF`cOWcj1d_d z?p8&L#SPCMInkka8f@#~q!e#c4etFeJ*Fn4c+@_JExqNzle3@U-eHG5FR*~%FmL(!p7HbJK&GaGoaf~QJdcBw zRRmv8C|qmQY{yD0u}AHQC1}o)i(m~qujcu5;p++W{slY1v-BK=ude1#-8NJGvvn{k zj@~Z*@}=4L`G^s-_(aS7t1Tnj=+Ou@wfqD0``Jk^fIAMN!b<_ISU!5BN z$-FjD$XuSfbEY(KbI55pVNW2WZc-1v5S2e7#ba2~Rie#8Wg>I*MB(DGWDwN3UD&n~ zot|Ahhq2cyp1fg>o!>llzkkR<8e!53oTO(ej*Ly+KbJpseqY#caH5!y6j>AvUKAA- zC*xH2iz2<^@(e4ASdEGzanU$51o43|xTGc%iXoIM2G>G*E7GYda)ii(y={_f$Tn@raT_POn$(8$s_cvVb6neFxAQg64049p z-d)oi3N@t1*>mPfA)Lbx?}H=wUreYF7_2r%>P@lyA~{V z4*Q=L{{p2RfP(i8!#3#$x(C$R(RuWDT{_`Kj?s`OILM``;Y5rytYD6HaCUiNWSAonEedfrWYLH*+0iq~Xn8J;T z!o+uTm`YYky{GeMhRS<%7;LmtjYVaREUTP5pB^<+m-7#L^EW5j;8kI<+%6^K`o3^I zSqUKWJQb^`c!7%ft$?hiHu{ztZkfInR!1NKYNbgeG_^(uj?^9_ia6IOSZa0=c$Vy* z;f-mc6xCx#WbzVqvp5%~8f~eTpjWE>3gSQVJli!G4U-nG%s9|Mx?Ur2q z)@LVF*gy)!PJc_sDn41(WGV~Nwfod6kx#?%*=ceixrRHU2CgvsO z>nCQG6esH!7bWYb_r?vDFB+ZRlWcK literal 0 HcmV?d00001 diff --git a/src/flask_prompt_master/admin/models/__pycache__/admin_user.cpython-312.pyc b/src/flask_prompt_master/admin/models/__pycache__/admin_user.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..61adfde5fa7e2f15128abb629c042abffe2bcbc4 GIT binary patch literal 4025 zcmb^!TTC3+_0G(`c`OjX>>4n*Y3g-tmQcrzNo1*hhD0WkjoWxzYqabg!{FIjd}lWC zBJ6G|D_UwNEODD%L{e<2YBvShII?0?w@N>0`%w@`Y$uHrsa+m_j%j}S)pKWdhc$*M zmU;!3bI>X^e=iSEL$E}>jfZ%FbW{dU{+$nOn_l1%t>sR z3vfDYkt|_rz^cPm$ri8y-747wb{3sL*mf0RyI}3BC@5e5D{vDQ;}meltUgC{8=PFr zw{I;c-^q=nmw%aAO59({+{*p>nroFZ`WVfDgQ}p4VF6(08ATu`#Y>_L2NrjCh74`S zZhpigmVShQ0t|czihFy~-6P%$rQ1aB8N=;4Fs>x;c)dZ=ph8S|^$-yq$ziFEa3jTxuPr;`<``kvcFV{MT&)&CETTimm4vd;8b z4Q|Gst4yF4d#|FvHoP77I$RICNATi?t7sx`Xka^>8;j2Mcn5Gb#F~6NHRlg;Sd?jz zL}^lPD6ExR8O;@>9DEd5x}b(i`iU40k{EwMh^@AP$nmI?3hITJOTO#lG+R^_FGdB; zAx9-C*ewZ~JroIt1zBB<18bC4RPN3@%QxT7-ALr7ulra+QKSsERZKZ(U9SH8_VVq! ztJMH%R^0|2z4E*FSB5_Eak_|R$HMtwR8nV|rO)C^_a1;^`cVdvLQ5aU zbIJFXM<3+=^sA-Rr>m)ZK4x7sml(@E_=ABrbdB9x`d}*0$mzN?M?{YH(*~oa3mhs z6#6|)bp7`ge{Uo#_=zC*i9xwL;tz_d67nk~g6j8-+}7!GU^5_xv9LNFg=fNfGKR<^mdBm?`+YuQ+-J(X5)w ztZL7rP#+AxjSHg74gj*RWFrNcI?Q`$W^Y)9>cqMhL)U5KH5ChE#R4n!giVCIT1r207Rbx?t zlB!TwL5K-$t)v#Vfx4o=JeEy6fU2B;Esp-}t{fg18c6TFc4g7sJm+qncYh~g`KP;f z_{z|g^rg(Rqd!}0+&|a2f4=d>dH2CZ_mMgGk$LyA8E($~+6_W(lup^0 z?wRxMS@gEedE2IbFz-DukclWNv7B29sr7K5@NVXRTg{;o@1i) zb+z)|{oL>e#$4$*>SHxdQHjQi_+v(Qk3uiYyTR5YsJw^=D%R83zSozH^}DW%IHojX4C69CTjwamy$k;5&|EMaRbGnph%%ubgy)bNk|S?vt`HGu za_~V_a~W?43X=w^opxcemu~b;1*4fn`URrK$O{yAfNt${)8kEhsL{M>9ud0Hj-k@n2K1!rsgaMoRyAj7etSlW}W8uyHPZnljz zWYlS4<|hm8v+-B5)eXtRsg9A3w3zN57e~bmHzrR-|12+5pNXH$mewQ=4|fc8B*kQR zN*obyayR5m*YvTO7Z*yq;>WTj)rq#@gF^?CXOpK=XGhMa$&GWFGt(z#-dHI4QT%AO z!joiE&JkyN=lJf?-5EUHxlr+1ydzubO;)8mBc62I_)DWN&9)w0s5};bbpv|Jao>H{ zJ=3^Q`NmeXU4Qrg#XlRmxKK&rudZ1vR?nkS&U%`8' diff --git a/src/flask_prompt_master/admin/views/__init__.py b/src/flask_prompt_master/admin/views/__init__.py new file mode 100644 index 0000000..060878a --- /dev/null +++ b/src/flask_prompt_master/admin/views/__init__.py @@ -0,0 +1,14 @@ +""" +后台管理视图包 +""" +from .user_admin import UserAdminView +from .prompt_admin import PromptAdminView +from .template_admin import TemplateAdminView +from .system_admin import SystemAdminView + +__all__ = [ + 'UserAdminView', + 'PromptAdminView', + 'TemplateAdminView', + 'SystemAdminView' +] diff --git a/src/flask_prompt_master/admin/views/__pycache__/__init__.cpython-312.pyc b/src/flask_prompt_master/admin/views/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..79463ea6f4b13a6809b498fdcb2ff21d7c6ab5de GIT binary patch literal 474 zcmYjNyH3L}6t(jvv=k;rHlz$-u_FWnD?;KiWU)eHXuwV)I|X%Qpc8^_3{0>f79I;D zKfoVE>V%@g22?Ce*ijI@!_m1%*XP{pmwMd=vYzfATMH0?SJnKLR*~VdBo`nAJ_uo` zQOGo3gNo}^XNGT-+@L12e5>RpwVC5P5bS}_x`zH($M&4|w6t-H-dS<}Fu1!K^tx|1 z-Qx1OI6ZOSWj63&Uf&XguY@d$w6s?N%2$%E;WSop25UHrl}l+oi*q>Nl=U1g;3DjKJfIhF zNiX6-eE@zXyrC)SgSd< z+`7Zk|0AahrucAtCN4J3WgGLH?ahRN5s=w{4>XBKC;)(CJ0svRM3O~GR=wev?6?v4%WzE=5L%CJsGo%Hvw9Lq+B(OJ;zG+JjM-BTIbB^xm+@!9ufXkW z3`7h4SsHy%+I_#Y``1$A_6rXt_T8V@*E!JWOf))LV+~S}wH~7gZQ*RAISFtY0kwbQ zYVXUBd*>GW7jHwSmp-5GU7LqdoctEm6e=>f((tLv)29O^ttV?(ezG4{=EwrklY>|$FK3UtyHN)^smly`m8RE#$5qiNePK2hD zL^6hfeakSY;20HQ)EszL|^A~dNO#z=_aVHZHmqu-P<0IFDNy$M^P+S=bORa*xitElp> z)O!2(fpT}#j_#J1x|J#@kMin}+B&jYMq|4=yZ3aedppV&_-^>cD~CEmu|SJX&vlt7-}5`$JW~7k&d!?RYN0N7f&o4?~ZQ2GO@VpQ93oEc9e~eb8@;V zt!b%r+_dPSleH|}3Gd`1@d(5W7ehwvsd09~F{vOR&&OfMZ`xiwb30*f#-B4c;}M-$ zvc;Vif9o+g&jQA^Juy>Wr(#INn6e!pR#TEBJyK*zd7>hz{t)eWh+ch&s(&djOT`8I J34$$x{U2z$D!~8% literal 0 HcmV?d00001 diff --git a/src/flask_prompt_master/admin/views/__pycache__/system_admin.cpython-312.pyc b/src/flask_prompt_master/admin/views/__pycache__/system_admin.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c787c71373f286fb55e8e4200df2869a1985cfea GIT binary patch literal 4019 zcmcguZ%iD=6`#G`y?-2civhdgFdTpF!{m-)8dH+mB2{D3y0{f_+gy=X%k9G5W%mwd z_l&_1rHK_mBQ^1tQWQ%R^^Y6ba#FQQD_Ls#(Ne##y-Igo5jj=kV1GLoqE_yg&fC3( z11@&c51l)`c{B6oy|**J_vZbny4s6Kd;i^ECw@|e(BENTH@3uV&Jtz`i719dN_5DS z;)ppEI!044re#K9W31wgIn6O6yA*fKt$1P{#T)aQbF5sY_+mbjcgod@Kjt@immE+6 zv4FzGI0`w?X(YNYBhe!{uG5z7Sj~(#T$QaOBk#sn&G$l7bIm;7BneL84hubP-Z-vj)Mh}%xXzRGCV*uVPD+>; z;2aE}Y95L;dgwPNqkTQcBMD8BXM@(bBqIuHVk{$I(Oe{#&8nK9sbLm7i4}uOs=ScZ z5(G2JIJte%U~op33@1U6NT5^0Pe>YH(q-**7>p{(#7~+CKK=+>m-M{tadj|EM!L{cTdQYNIduZwDD^IUAM{~73YrdYM6V=q` zxlWzy%yCB-*pC9WZ(drwv=;3DBfD@Z7kF;X{oE!|iDua-2`6)93KL~F93OhkT8XVq*Eb$Lb#Do zgMlrxb$f3BW=+wr5TVa^0bHGRyFneB6bH~W~q6|qiIVkx0N(l6(KcLo|@-fSX!!)a@t zC1t_9OM_4_TdKk4cKQ^ca!=C^+Y>1Wx;?E7eE{=4&9DU(DX3D((pU=nkGj(KJ?KWehciufm%{k`i zIcAQXbI!R%&sEn5YD3ylTeKbFx_OW2-T6(B((9@#?syT2Rr8*gJtL)lnwmxz=@-#7 z74~HT5%+%dySs1xxKwJ~TDf;~_2Um#Kl#DC_W_pV9d*KMm{ngOtd~5ZqBJgKW$iwI z8A>Wqm{EahlEKC^*|ZjR8C66vYtuRAVZ>{^lGX`z*;4R zkXg8?Moq~azcW=U3Q2i}l!6mURl|ao%%oKe>2Ek(kHIU0oytmh2E*YPp7EqiUSfW1 z217zKoYvUz#<2ut05515GLAu~Gt(IE07Ht#)j*({He9Nts>BEe0FlD8w9hObSy>p9 zWP{EoMZ-OTGucUE$e;yTHauUu7?&o&yRg@McyJh0b^$pCq#MYSKq7<~epR|4rFl3` zUYN+>C`?k43O%r!gf#rdGFscWm?@g@*qq*Rh^TAVk@Ki21xNv66i4M1|oLN4TZ;a@Tkz8ZX z!Z+6&TdkR>-WXly4(7QQooo5|_pXhuj{fX?zN267=+AWw=-j|2?Kte;@YgO%Z%hkfj_b~Iy*k(X-cvbl02aMhyjS~H`j`82Tzj7D(z&i*oyc*=H(9#P|4%0h)Gv+` zG3#@FdR`|hv?Ds91+W#|okDLQ4exZM}t#rwT^~9t7NV z{vvW$`~S6d0C7+J19?3DkD5b;P)nhu75;7Qg^td`k+3QGDM&@=Syqn@D?^zAQFz@K0V9S(a8H_xX9MdEqG%kxS`%*w#~cz!A?$fXq*&x@Hj z&tpik7-|X(sU3%a0Gi%GW~pMq3tn3c1Oh|-Iw6T}Qd4AL{GbjweG8{w<4p-C`=xDx`XU2EF2edPGoXApd;kxU-l(!FXS0^W@Cun; z+S8;MOQtQC%|gC|)Y_2>wRVJjx+JTm&P)Vm#GB!49Qur1R)~AmDOr%?2}zkTquk60 zRthlB;#qJCZo?`Y-OpIZd=d|lIY^Ugl#n7#QPcy*K`{hPRQL|+yMu;xH2fjz{1652 Tprc#NX^Qf{mG~4vZz}u`t*K^T literal 0 HcmV?d00001 diff --git a/src/flask_prompt_master/admin/views/__pycache__/template_admin.cpython-312.pyc b/src/flask_prompt_master/admin/views/__pycache__/template_admin.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..078a7cece4fec2e3e38865eca66facd9c724743f GIT binary patch literal 2039 zcma)6+iw(A7(X*RyF1-$uS=n&Euq9LA>HsmjFAu}pa{~KNQ{}6$#l-y?ciLNb7m;T z#;ma+P1M-Lhbr>0wH0kKtE0eF5k?Ba0v1id5*1P4e8h}ck|G7XWJax+ z5)1gK8Mop}TtFgv9pTtC!f_&AjfCeZ@}!(^^SVJwEPZov>D=w*g^SCxr&g|C1+VJ>~+JC{w3M7&9E(PZ&(a zq^NnO>nC(dz#FTY>x*yfd+MUb?5E3DzViSAs4;VD`TDJZUAg$l{qN6&?1upxgf@4r zF?+drYi?!XQgB3LX1;Ojho$PxX7xs1q8w2)$qDzSX(0LNIt9&eQD))n)3ZD@=9LLmgC_ZD-LYK` z)j-TgC_mC~hc0NII}QeeHw?N*9g_oBV#F*mp25)HhYI74MGAoQqlRXWIR(vdnOHEf znFv5eJ3s_&Ki5h{hgyEr;?GPlC9UCLJhvfObTfC$I<^}4sQS2Om&nO2&lYl>%ggW` z+XAG5{z&%R8+_sX)aj}D4`%lNlIp+LxApw))4S*Q%tU{cdnqsYXujK*Rh3V(s`?3) zU+0;ePpRrLPcvJVxT<1DS5?X%X1bLVK5D*9Y+QB>+x3&WV|tdYng(+@5JN#5BPR26 zEs+t8LM8x-&@I!_7Y!3I#r&?8+v+Y45j-j`(aCM zs_d(02P+32#ZZ?nR1W=~>8!U8)VurnFE>!n4a4tYR_=*a4nNAFfj5PkvK#3mgP<7LJ~vvEHa?Ki#=#51?ECdiLl?Hp^*xM6dSf-I z`%yxSmV{MVif#|%<)Mz|+h`tkzc-xA@O|c!PmQq=cFfc$$ElwT&8AZ_?C>Gj2E#n^ zoM9dbFveP2*kTUeurj+i$FUM(Qr;axWDk7_bbQv>FpvikK@c8Fq98qvA)&B{h8NK* Ti)iy-(k@~9T-##=TX4$10)bor literal 0 HcmV?d00001 diff --git a/src/flask_prompt_master/admin/views/__pycache__/user_admin.cpython-312.pyc b/src/flask_prompt_master/admin/views/__pycache__/user_admin.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b20420f1a16d649a6764644d78b551df292df357 GIT binary patch literal 2214 zcmZ`)%WoS+7@zg7?X_daiSvGy^w0`JWfQ3a4h2EgH)@1L;jow0W@plDyX$plcH1QC z!G)*_6le}eZfSXx7Nw|CprW)A2lxZHq)4b{L<9)zG`BVZA#viHT_uosUD1ZHQUsxbFwzi~aKx6Jh!$~VO_qc%+lmv_qJgZ~ zF(u-DiY?v+{b#BX|Tf6&oJ$EQgq36d_GClobzz3L;38pbQZ# zX);l?D2`|`ENk(@2rHjSnu?=1hT~Yp37o`DIE9sqs+It28mvj+&A^**MoZxqEsa~X zX82`bKMVUUxNQn)t+*X%7Iy$`o9N7S@wABZW$ai*k(i%_NFFVVpW3iJnKZ;F`5;-9 z%f5xVTC~igMZ+OH8P+C{qr@>Ro5vh?#IgyO85!p><{6&Ppk|anU9c`H38dun@g1&IfXXEc{C$Piy*B)ax!fbvdEqaXyhURJGAFePT`3#oze-sni(@} zkH<^YbxI!NiBUrEh+!UOrUc)vt_TPMbSnpdRFLR#xQ+X*y)Pd>jp_x)03uKv+mOXp_*lugVp5vm8h;Yrb)P=@^A`61q7G2QUJF~F&1 zipq0^y0*Z!!iYV}MEHaM?k$YD4k-Ydk61==#4Q+>$IJqwW?|Ge*ik)9Tz3rS5n2dt zD;x*(u)>2Y^Q8%%7T1|3Vayt_$z`pk1O>xaRjWB5q+%@6VikPh3$sz z`qfLdkI&T3o~vKD0hn3*aH@7?3I@b56j4kwE*ISsJmr_flMbN^pxPuX%BV{n9(BZ+ z;7Q1@fo~?_9fU7gx|)Znf!8m(E{lWH|HwgWSFlh!;7~tw5MKYc<-e zF$!I71PqB0k4z4(5BO1!7sM$7a%FTch7#!@#Nk{!S9M*C1zqP!U7YFLLQd)WG2gHo zmbk8C*VJ_?CPEMpSAtohk|Hj-R?*{4rfd67QMWDT@${- z^0XjN2_pIuw493h84w|`Q$VKB&q_bkCf!=z4J_4L-c`-?mG>>jP}`VPe&x4JPqn?j z+PS&f+EdLAR5M-GY;U!D>;2Y5N348sxf^YIMVeQ7!N2pl@-Q6HH#Dzw0nc`o4^%U4 z<$cxGj`9Jp54;MFJ$jy|)Jl%ruwf|!PL8CrmlcvJYig9l9oeyG3??|x`I zZ@NW~S|fRO%r>a_4`2N%7?M=X?Qqs?fq9XC%)H14&}R)6^q6=3k1rLoKX`Rv1=m-V zt2;sjy+~gIKk?|9@VF;Sl5}5*NXkDkB;^)R{#Uep0X@5bwl1K7zm)A#dS?IM2w2de F{{ZC_m~8+6 literal 0 HcmV?d00001 diff --git a/src/flask_prompt_master/admin/views/prompt_admin.py b/src/flask_prompt_master/admin/views/prompt_admin.py new file mode 100644 index 0000000..dba4bb1 --- /dev/null +++ b/src/flask_prompt_master/admin/views/prompt_admin.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- +""" +提示词管理视图 +""" +from flask_admin.contrib.sqla import ModelView +from flask_login import current_user +from src.flask_prompt_master.models.models import Prompt +from datetime import datetime + +class PromptAdminView(ModelView): + """提示词管理视图""" + + # 设置模型 + model = Prompt + + # 设置页面标题 + name = '提示词管理' + endpoint = 'admin.prompt' + + # 设置列显示 + column_list = ['id', 'input_text', 'generated_text', 'user_id', 'created_at'] + column_labels = { + 'id': 'ID', + 'input_text': '输入文本', + 'generated_text': '生成文本', + 'user_id': '用户ID', + 'created_at': '创建时间' + } + + # 设置搜索 + column_searchable_list = ['input_text', 'generated_text'] + + # 设置过滤 + column_filters = ['created_at', 'user_id'] + + # 设置排序 + column_default_sort = ('created_at', True) + + # 设置编辑权限 + can_create = False + can_edit = True + can_delete = True + + # 设置表单字段 + form_excluded_columns = ['feedbacks'] + + # 设置文本字段为文本区域 + form_widget_args = { + 'input_text': {'rows': 3}, + 'generated_text': {'rows': 5} + } + + def is_accessible(self): + """检查访问权限""" + return current_user.is_authenticated and current_user.role in ['super_admin', 'content_admin'] + + def on_model_change(self, form, model, is_created): + """模型变更时的处理""" + # 可以添加内容审核逻辑 + pass diff --git a/src/flask_prompt_master/admin/views/system_admin.py b/src/flask_prompt_master/admin/views/system_admin.py new file mode 100644 index 0000000..6619452 --- /dev/null +++ b/src/flask_prompt_master/admin/views/system_admin.py @@ -0,0 +1,83 @@ +# -*- coding: utf-8 -*- +""" +系统管理视图 +""" +from flask_admin import BaseView, expose +from flask_login import login_required, current_user +from src.flask_prompt_master.models.models import User, Prompt, PromptTemplate +from src.flask_prompt_master import db +from sqlalchemy import func +from datetime import datetime, timedelta + +class SystemAdminView(BaseView): + """系统管理视图""" + + @expose('/') + @login_required + def index(self): + """系统概览""" + if not current_user.is_authenticated or current_user.role not in ['super_admin', 'system_admin']: + return self.render('admin/403.html') + + # 获取统计数据 + stats = self._get_system_stats() + + return self.render('admin/system_dashboard.html', stats=stats) + + @expose('/logs') + @login_required + def logs(self): + """系统日志""" + if not current_user.is_authenticated or current_user.role not in ['super_admin', 'system_admin']: + return self.render('admin/403.html') + + # 这里可以添加日志查看功能 + return self.render('admin/system_logs.html') + + def _get_system_stats(self): + """获取系统统计信息""" + try: + # 用户统计 + total_users = User.query.count() + active_users = User.query.filter_by(status=1).count() + + # 提示词统计 + total_prompts = Prompt.query.count() + today_prompts = Prompt.query.filter( + Prompt.created_at >= datetime.now().date() + ).count() + + # 模板统计 + total_templates = PromptTemplate.query.count() + default_templates = PromptTemplate.query.filter_by(is_default=True).count() + + # 最近7天用户注册趋势 + seven_days_ago = datetime.now() - timedelta(days=7) + daily_registrations = db.session.query( + func.date(User.created_time).label('date'), + func.count(User.uid).label('count') + ).filter( + User.created_time >= seven_days_ago + ).group_by( + func.date(User.created_time) + ).all() + + return { + 'total_users': total_users, + 'active_users': active_users, + 'total_prompts': total_prompts, + 'today_prompts': today_prompts, + 'total_templates': total_templates, + 'default_templates': default_templates, + 'daily_registrations': daily_registrations + } + except Exception as e: + return { + 'total_users': 0, + 'active_users': 0, + 'total_prompts': 0, + 'today_prompts': 0, + 'total_templates': 0, + 'default_templates': 0, + 'daily_registrations': [] + } diff --git a/src/flask_prompt_master/admin/views/template_admin.py b/src/flask_prompt_master/admin/views/template_admin.py new file mode 100644 index 0000000..4c05e95 --- /dev/null +++ b/src/flask_prompt_master/admin/views/template_admin.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- +""" +模板管理视图 +""" +from flask_admin.contrib.sqla import ModelView +from flask_login import current_user +from src.flask_prompt_master.models.models import PromptTemplate +from datetime import datetime + +class TemplateAdminView(ModelView): + """模板管理视图""" + + # 设置模型 + model = PromptTemplate + + # 设置页面标题 + name = '模板管理' + endpoint = 'admin.template' + + # 设置列显示 + column_list = ['id', 'name', 'category', 'industry', 'profession', 'is_default', 'created_at'] + column_labels = { + 'id': 'ID', + 'name': '模板名称', + 'category': '分类', + 'industry': '行业', + 'profession': '职业', + 'is_default': '是否默认', + 'created_at': '创建时间' + } + + # 设置搜索 + column_searchable_list = ['name', 'category', 'industry', 'profession'] + + # 设置过滤 + column_filters = ['category', 'industry', 'profession', 'is_default', 'created_at'] + + # 设置排序 + column_default_sort = ('created_at', True) + + # 设置编辑权限 + can_create = True + can_edit = True + can_delete = True + + # 设置表单字段 + form_excluded_columns = ['updated_at'] + + # 设置文本字段为文本区域 + form_widget_args = { + 'description': {'rows': 3}, + 'system_prompt': {'rows': 8} + } + + def is_accessible(self): + """检查访问权限""" + return current_user.is_authenticated and current_user.role in ['super_admin', 'content_admin'] + + def on_model_change(self, form, model, is_created): + """模型变更时的处理""" + if is_created: + model.created_at = datetime.now() + model.updated_at = datetime.now() diff --git a/src/flask_prompt_master/admin/views/user_admin.py b/src/flask_prompt_master/admin/views/user_admin.py new file mode 100644 index 0000000..8c7b8c7 --- /dev/null +++ b/src/flask_prompt_master/admin/views/user_admin.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- +""" +用户管理视图 +""" +from flask_admin import BaseView, expose +from flask_admin.contrib.sqla import ModelView +from flask_login import login_required, current_user +from src.flask_prompt_master.models.models import User +from src.flask_prompt_master import db +from datetime import datetime + +class UserAdminView(ModelView): + """用户管理视图""" + + # 设置模型 + model = User + + # 设置页面标题 + name = '用户管理' + endpoint = 'admin.user' + + # 设置列显示 + column_list = ['uid', 'nickname', 'login_name', 'email', 'mobile', 'sex', 'status', 'created_time'] + column_labels = { + 'uid': '用户ID', + 'nickname': '昵称', + 'login_name': '用户名', + 'email': '邮箱', + 'mobile': '手机号', + 'sex': '性别', + 'status': '状态', + 'created_time': '注册时间' + } + + # 设置搜索 + column_searchable_list = ['nickname', 'login_name', 'email', 'mobile'] + + # 设置过滤 + column_filters = ['status', 'sex', 'created_time'] + + # 设置排序 + column_default_sort = ('created_time', True) + + # 设置编辑权限 + can_create = False + can_edit = True + can_delete = True + + # 设置表单字段 + form_excluded_columns = ['login_pwd', 'login_salt', 'prompts', 'feedbacks'] + + def is_accessible(self): + """检查访问权限""" + return current_user.is_authenticated and current_user.role in ['super_admin', 'user_admin'] + + def on_model_change(self, form, model, is_created): + """模型变更时的处理""" + if not is_created: + model.updated_time = datetime.now() + + def after_model_delete(self, model): + """删除模型后的处理""" + # 记录删除日志 + pass diff --git a/src/flask_prompt_master/models/__pycache__/__init__.cpython-312.pyc b/src/flask_prompt_master/models/__pycache__/__init__.cpython-312.pyc index 531e32d73e3a51089a9524729b2863578152cce1..2829091a1701c19f8d0e04eb11c5ccd882375e33 100644 GIT binary patch delta 20 acmaFK^pc7DG%qg~0}zzV+qjWCju8MqRRymA delta 20 acmaFK^pc7DG%qg~0}woJ-LR26ju8Ms!v+KZ diff --git a/src/flask_prompt_master/models/__pycache__/favorites.cpython-312.pyc b/src/flask_prompt_master/models/__pycache__/favorites.cpython-312.pyc index d967b6669ca5ab78abc33eb7aef2377240f7c136..40bdd3c71a0276007b2149ca666b8ceee3f13323 100644 GIT binary patch delta 20 acmew;@lk^NG%qg~0}zzV+qjWCjRycjxCQ}hMsAL+09X76H2?qr delta 22 ccmbRIifQsIChpU`yj%=G@VIruMsAL+09tAXiU0rr diff --git a/src/flask_prompt_master/services/__pycache__/auth_service.cpython-312.pyc b/src/flask_prompt_master/services/__pycache__/auth_service.cpython-312.pyc index b04567acdfdd4f327400b635e55e8b8e875b39fd..f55026612ed4514a8c2759dafd700602f5613856 100644 GIT binary patch delta 20 acmeAS?hNKW&CAQh00brTHg4pu(*yuK;RV?M delta 20 acmeAS?hNKW&CAQh00fU)H*Dmt(*yuNPX;ak diff --git a/src/flask_prompt_master/services/__pycache__/favorite_service.cpython-312.pyc b/src/flask_prompt_master/services/__pycache__/favorite_service.cpython-312.pyc index bac5462b7178c4d8ed98981c52bbf451be81c8a1..6a23bf44da1f1b712df30af1204fcbce5aa9af97 100644 GIT binary patch delta 20 acmaFp^3a9*G%qg~0}zzV+qjWCObGx-eg-@M delta 20 acmaFp^3a9*G%qg~0}woJ-LR26ObGx<>;{nl diff --git a/src/flask_prompt_master/templates/admin/index.html b/src/flask_prompt_master/templates/admin/index.html new file mode 100644 index 0000000..f5497de --- /dev/null +++ b/src/flask_prompt_master/templates/admin/index.html @@ -0,0 +1,116 @@ +{% extends 'admin/master.html' %} + +{% block title %}管理后台首页{% endblock %} + +{% block body %} +
+
+
+

+ 系统概览 +

+
+
+ + +
+
+
+
+
+ 快速操作 +
+
+ +
+
+
+ + +
+
+
+
+
+ 系统信息 +
+
+
+
+
+ + + + + + + + + + + + + +
系统名称:提示词大师管理系统
当前用户:{{ current_user.username if current_user.is_authenticated else '未登录' }}
用户角色:{{ current_user.role if current_user.is_authenticated else '无' }}
+
+
+ + + + + + + + + + + + + +
最后登录:{{ current_user.last_login.strftime('%Y-%m-%d %H:%M:%S') if current_user.is_authenticated and current_user.last_login else '首次登录' }}
登录时间:{{ current_user.created_at.strftime('%Y-%m-%d %H:%M:%S') if current_user.is_authenticated else '无' }}
账户状态: + {% if current_user.is_authenticated %} + + {{ '正常' if current_user.is_active else '禁用' }} + + {% else %} + 未登录 + {% endif %} +
+
+
+
+
+
+
+
+ + +{% endblock %} diff --git a/src/flask_prompt_master/templates/admin/login.html b/src/flask_prompt_master/templates/admin/login.html new file mode 100644 index 0000000..7f8fbb4 --- /dev/null +++ b/src/flask_prompt_master/templates/admin/login.html @@ -0,0 +1,137 @@ + + + + + + 管理员登录 - 提示词大师后台管理 + + + + + + + + + + diff --git a/src/flask_prompt_master/templates/admin/system_dashboard.html b/src/flask_prompt_master/templates/admin/system_dashboard.html new file mode 100644 index 0000000..c867874 --- /dev/null +++ b/src/flask_prompt_master/templates/admin/system_dashboard.html @@ -0,0 +1,223 @@ +{% extends 'admin/master.html' %} + +{% block title %}系统管理 - 仪表板{% endblock %} + +{% block body %} +
+
+
+

+ 系统仪表板 +

+
+
+ + +
+
+
+
+
+
+
+ 总用户数 +
+
{{ stats.total_users }}
+
+
+ +
+
+
+
+
+ +
+
+
+
+
+
+ 今日生成 +
+
{{ stats.today_prompts }}
+
+
+ +
+
+
+
+
+ +
+
+
+
+
+
+ 模板数量 +
+
{{ stats.total_templates }}
+
+
+ +
+
+
+
+
+ +
+
+
+
+
+
+ 活跃用户 +
+
{{ stats.active_users }}
+
+
+ +
+
+
+
+
+
+ + +
+
+
+
+
+ 系统信息 +
+
+
+
+
+ + + + + + + + + + + + + + + + + +
系统名称:提示词大师管理系统
当前用户:{{ current_user.username if current_user.is_authenticated else '未登录' }}
用户角色:{{ current_user.role if current_user.is_authenticated else '无' }}
系统状态:正常运行
+
+
+ + + + + + + + + + + + + + + + + +
最后登录:{{ current_user.last_login.strftime('%Y-%m-%d %H:%M:%S') if current_user.is_authenticated and current_user.last_login else '首次登录' }}
登录时间:{{ current_user.created_at.strftime('%Y-%m-%d %H:%M:%S') if current_user.is_authenticated else '无' }}
账户状态: + {% if current_user.is_authenticated %} + + {{ '正常' if current_user.is_active else '禁用' }} + + {% else %} + 未登录 + {% endif %} +
数据库状态:连接正常
+
+
+
+
+
+
+ + +
+
+
+
+
+ 快速操作 +
+
+ +
+
+
+
+ + +{% endblock %} diff --git a/src/flask_prompt_master/templates/admin/system_logs.html b/src/flask_prompt_master/templates/admin/system_logs.html new file mode 100644 index 0000000..8d54380 --- /dev/null +++ b/src/flask_prompt_master/templates/admin/system_logs.html @@ -0,0 +1,86 @@ +{% extends 'admin/master.html' %} + +{% block title %}系统日志{% endblock %} + +{% block body %} +
+
+
+

+ 系统日志 +

+
+
+ +
+
+
+
+
+ 日志列表 +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
时间级别模块消息操作
2025-08-29 22:34:44INFO系统管理管理员登录成功 + +
2025-08-29 22:33:42SUCCESS应用启动Flask应用启动成功 + +
2025-08-29 22:33:42INFO数据库数据库连接成功 + +
+
+
+
+
+
+
+ + +{% endblock %}