Files
pear-admin-flask/plugins/friendManager/view/friend.py
T
bwstudio 0ffc103e46 feat(plugins): 4 个业务插件代码 + 前台公开页面改造
plugins/navManager:导航 CRUD + 导航分类 CRUD(独立 blueprint)
plugins/friendManager:友情链接 CRUD
plugins/aboutManager:关于本站 CRUD
plugins/siteStats:访问统计主页 + JSON 接口 + /site/* GET 埋点 + nav 卡片点击埋点

applications/view/public/nav.py:导航聚合页 / 分类详情 / JSON 接口,支持 visit_count 注入
applications/view/public/about.py:关于本站公开页
applications/view/public/friend.py:友情链接公开页

templates/public/base.html:响应式首页(搜索框 / 分类抽屉 / 5 主题切换 / footer 隐私小字)
templates/public/index.html / category.html:卡片右下角访问次数
templates/public/about.html / friend.html:关于 + 友链前台页

docs/plugins-development.md:插件开发完整指南(生命周期 + 4 个示例 + FAQ + framework 迁移)

scripts/:探针与端到端验证脚本(probe_*/verify_*/test_*)
2026-09-06 18:18:06 +08:00

169 lines
5.4 KiB
Python

"""
后台友情链接视图 — 插件版(不依赖 framework 系统蓝图)。
URL 前缀变更:原 `'/friend'` 改成 `'/system/friend'`,独立挂载到 app。
"""
import os
from flask import Blueprint, render_template, request
from flask_login import current_user
from applications.common import curd
from applications.common.utils.http import table_api, fail_api, success_api
from applications.common.utils.rights import authorize
from applications.common.utils.validate import str_escape
from applications.extensions import db
from applications.extensions.init_limit import limiter
from applications.models import Friend
from applications.schemas import FriendManageSchema
dir_path = os.path.dirname(os.path.abspath(__file__))
bp = Blueprint(
'friend', __name__,
url_prefix='/system/friend',
template_folder=os.path.join(dir_path, '..', 'templates'),
)
@bp.get('/')
@authorize("system:friend:main")
def main():
return render_template('system/friend/main.html')
@bp.get('/data')
@limiter.limit("60 per minute")
@authorize("system:friend:main")
def data():
keyword = str_escape(request.args.get('keyword', type=str))
category = str_escape(request.args.get('category', type=str))
query = Friend.query.filter()
if keyword:
query = query.filter(
db.or_(
Friend.title.contains(keyword),
Friend.url.contains(keyword),
Friend.description.contains(keyword),
)
)
if category:
query = query.filter(Friend.category == category)
items = query.order_by(Friend.category.asc(), Friend.sort.asc(), Friend.id.asc()).layui_paginate()
return table_api(
msg="请求成功",
data=curd.model_to_dicts(schema=FriendManageSchema, data=items.items),
count=items.total,
)
@bp.get('/add')
@authorize("system:friend:add", log=True)
def add():
return render_template('system/friend/add.html')
@bp.post('/save')
@authorize("system:friend:add", log=True)
def save():
req_json = request.get_json(force=True, silent=True) or {}
title = str_escape(req_json.get('title'))
url = str_escape(req_json.get('url'))
if not all([title, url]):
return fail_api(msg="名称/链接不得为空")
try:
friend = Friend(
title=title,
url=url,
logo=str_escape(req_json.get('logo')),
description=str_escape(req_json.get('description')),
category=str_escape(req_json.get('category')) or '推荐友链',
sort=int(req_json.get('sort') or 0),
enable=int(req_json.get('enable') or 1),
is_external=int(req_json.get('isExternal') or 1),
create_by=current_user.username if current_user.is_authenticated else 'admin',
)
db.session.add(friend)
db.session.commit()
except Exception as e:
db.session.rollback()
return fail_api(msg=f"新增失败:{e}")
return success_api(msg="新增成功")
@bp.get('/edit')
@authorize("site:friend:edit", log=True)
def edit():
friend_id = request.args.get('friendId', type=int)
friend = curd.get_one_by_id(Friend, friend_id)
if not friend:
return fail_api(msg="友链不存在")
return render_template('system/friend/edit.html', friend=friend)
@bp.put('/update')
@authorize("site:friend:edit", log=True)
def update():
req_json = request.get_json(force=True, silent=True) or {}
friend_id = req_json.get('friendId')
friend = curd.get_one_by_id(Friend, friend_id)
if not friend:
return fail_api(msg="友链不存在")
title = str_escape(req_json.get('title'))
url = str_escape(req_json.get('url'))
if not all([title, url]):
return fail_api(msg="名称/链接不得为空")
try:
friend.title = title
friend.url = url
friend.logo = str_escape(req_json.get('logo'))
friend.description = str_escape(req_json.get('description'))
friend.category = str_escape(req_json.get('category')) or '推荐友链'
friend.sort = int(req_json.get('sort') or 0)
friend.enable = int(req_json.get('enable') or 1)
friend.is_external = int(req_json.get('isExternal') or 1)
db.session.commit()
except Exception as e:
db.session.rollback()
return fail_api(msg=f"更新失败:{e}")
return success_api(msg="更新成功")
@bp.delete('/remove/<int:friend_id>')
@authorize("system:friend:remove", log=True)
def remove(friend_id):
res = curd.delete_one_by_id(Friend, friend_id)
if not res:
return fail_api(msg="删除失败")
return success_api(msg="删除成功")
@bp.put('/enable')
@authorize("site:friend:edit", log=True)
def enable():
req_json = request.get_json(force=True, silent=True) or {}
friend_id = req_json.get('friendId')
if not friend_id:
return fail_api(msg="数据错误")
res = curd.enable_status(Friend, friend_id)
if not res:
return fail_api(msg="操作失败")
return success_api(msg="已启用")
@bp.put('/disable')
@authorize("site:friend:edit", log=True)
def dis_enable():
req_json = request.get_json(force=True, silent=True) or {}
friend_id = req_json.get('friendId')
if not friend_id:
return fail_api(msg="数据错误")
res = curd.disable_status(Friend, friend_id)
if not res:
return fail_api(msg="操作失败")
return success_api(msg="已禁用")