# 程序的主入口 # 承担服务器容器+程序作用 # 服务器容器:提供http容器服务,程序放置于该容器中运行 # 程序:本体-智能瞭望与智能问数系统 B/s架构 import os import sys import tornado.ioloop import tornado.web from tornado.httpserver import HTTPServer # from app.controllers.base import BaseHandler # 引入auth - controller层 from app.controllers.auth import LoginHandler,LogoutHandler from app.controllers.home import IndexHandler, DashboardHandler, DashboardApiHandler from app.controllers.user import UserPageHandler, UserApiHandler from app.controllers.menu import MenuPageHandler, MenuApiHandler from app.controllers.role import RolePageHandler, RoleApiHandler from app.controllers.model_engine import ModelEnginePageHandler, ModelEngineApiHandler, ModelTestApiHandler from app.controllers.spy_source import SpySourcePageHandler, SpySourceApiHandler from app.controllers.data_warehouse import DataWarehousePageHandler, DataWarehouseApiHandler, DataWarehouseDeepCrawlSseHandler from app.controllers.spy import SpyPageHandler, SpyRunApiHandler, SpyCommitApiHandler from app.controllers.api_manage import ApiManagePageHandler, ApiManageApiHandler, ApiInterfaceProxyHandler from app.controllers.digital_employee import DigitalEmployeePageHandler, DigitalEmployeeApiHandler, DigitalEmployeeChatApiHandler from app.controllers.chat_admin import ChatSessionsAdminPageHandler, ChatSessionsAdminApiHandler, ChatMessagesAdminPageHandler, ChatMessagesAdminApiHandler from app.controllers.skill_manage import SkillManagePageHandler, SkillManageApiHandler from app.controllers.portal import PortalRootHandler, PortalLoginHandler, PortalRegisterHandler, PortalChatPageHandler, PortalLogoutHandler, PortalModelsApiHandler, PortalEmployeesApiHandler, PortalSessionsApiHandler, PortalMessagesApiHandler, PortalChatStreamApiHandler, PortalAudioProxyHandler # 引入db - model层 from app.models.db import init_db, get_connection from app.models.user import UserRepository from app.models.role import RoleRepository from app.models.menu import MenuRepository try: if hasattr(sys.stdout, "reconfigure"): sys.stdout.reconfigure(encoding="utf-8", errors="replace") if hasattr(sys.stderr, "reconfigure"): sys.stderr.reconfigure(encoding="utf-8", errors="replace") except Exception: pass # class HealthHandler(tornado.web.RequestHandler): # def get(self): # self.write({"status":"ok"}) # class LoginHandler(tornado.web.RequestHandler): # def get(self): # self.write(f"""

模拟登录验证测试BaesHandler

#
# # """ # + self.xsrf_form_html() + # """ #
# """) # def post(self): # next_url = self.get_argument("next","/private") # self.set_secure_cookie("username","admin") # # 写完安全的cookie以后,跳转到目标地址 # self.redirect(next_url) # class PrivateHandler(BaseHandler): # @tornado.web.authenticated # def get(self): # self.write(self.current_user) def make_app(): # return tornado.web.Application([ # ("/abc",HealthHandler), # ("/login.jsp",HealthHandler), # ("/",HealthHandler), # ("/login.php",HealthHandler) # ],debug=True) # return tornado.web.Application([ # (r"/",LoginHandler), # (r"/login",LoginHandler), # (r"/abc",HealthHandler), # (r"/private",PrivateHandler) # ], # cookie_secret="demo-cookie-secret-change-me", # login_url="/", # xsrf_cookies=True, # debug=True # ) base_url = os.path.dirname(os.path.abspath(__file__)) settings = dict( # 预留view 层的内容配置 template_path=os.path.join(base_url,"app","templates"), static_path=os.path.join(base_url,"app","static"), cookie_secret="demo-cookie-secret-change-me", login_url="/admin/login", xsrf_cookies=True, debug=True, autoreload=True ) return tornado.web.Application([ (r"/", PortalRootHandler), (r"/login", PortalLoginHandler), (r"/register", PortalRegisterHandler), (r"/chat", PortalChatPageHandler), (r"/logout", PortalLogoutHandler), (r"/api/models", PortalModelsApiHandler), (r"/api/employees", PortalEmployeesApiHandler), (r"/api/sessions", PortalSessionsApiHandler), (r"/api/messages", PortalMessagesApiHandler), (r"/api/chat/stream", PortalChatStreamApiHandler), (r"/api/audio_proxy", PortalAudioProxyHandler), (r"/u/?", tornado.web.RedirectHandler, {"url": "/"}), (r"/u/login", tornado.web.RedirectHandler, {"url": "/login"}), (r"/u/register", tornado.web.RedirectHandler, {"url": "/register"}), (r"/u/chat", tornado.web.RedirectHandler, {"url": "/chat"}), (r"/u/logout", tornado.web.RedirectHandler, {"url": "/logout"}), (r"/api/u/models", tornado.web.RedirectHandler, {"url": "/api/models"}), (r"/api/u/employees", tornado.web.RedirectHandler, {"url": "/api/employees"}), (r"/api/u/sessions", tornado.web.RedirectHandler, {"url": "/api/sessions"}), (r"/api/u/messages", tornado.web.RedirectHandler, {"url": "/api/messages"}), (r"/api/u/chat/stream", tornado.web.RedirectHandler, {"url": "/api/chat/stream"}), (r"/admin/?", IndexHandler), (r"/admin/login", LoginHandler), (r"/admin/logout", LogoutHandler), (r"/admin/dashboard", DashboardHandler), (r"/dashboard", DashboardHandler), (r"/api/dashboard", DashboardApiHandler), (r"/auth/login", LoginHandler), (r"/auth/logout", LogoutHandler), (r"/user/list", UserPageHandler), (r"/api/user", UserApiHandler), (r"/menu/list", MenuPageHandler), (r"/api/menu", MenuApiHandler), (r"/role/list", RolePageHandler), (r"/api/role", RoleApiHandler), (r"/model_engine/list", ModelEnginePageHandler), (r"/api/model_engine", ModelEngineApiHandler), (r"/api/model_engine/test", ModelTestApiHandler), # 瞭望数据源管理路由 (r"/spy_source/list", SpySourcePageHandler), (r"/api/spy_source", SpySourceApiHandler), # 数据仓库路由 (r"/data_warehouse/list", DataWarehousePageHandler), (r"/api/data_warehouse", DataWarehouseApiHandler), (r"/api/data_warehouse/deep_sse", DataWarehouseDeepCrawlSseHandler), # 独立瞭望采集界面路由 (r"/spy", SpyPageHandler), (r"/api/spy/run", SpyRunApiHandler), (r"/api/spy/commit", SpyCommitApiHandler), # 接口管理 (r"/api_manage/list", ApiManagePageHandler), (r"/api/api_manage", ApiManageApiHandler), (r"/api/interface_proxy", ApiInterfaceProxyHandler), # 数字员工 (r"/digital_employee/list", DigitalEmployeePageHandler), (r"/api/digital_employee", DigitalEmployeeApiHandler), (r"/api/digital_employee/chat", DigitalEmployeeChatApiHandler), # 技能管理 (r"/skill_manage/list", SkillManagePageHandler), (r"/api/skill_manage", SkillManageApiHandler), # 用户侧会话/对话管理(后台) (r"/chat_admin/sessions", ChatSessionsAdminPageHandler), (r"/api/chat_admin/sessions", ChatSessionsAdminApiHandler), (r"/chat_admin/messages", ChatMessagesAdminPageHandler), (r"/api/chat_admin/messages", ChatMessagesAdminApiHandler), ], **settings ) if __name__ == "__main__": # 启动服务之前,检查并初始化数据库表 init_db() # 注入默认超管角色 roles = RoleRepository.get_all_roles() super_role_id = None for r in roles: if r['code'] == 'super_admin': super_role_id = r['id'] break if not super_role_id: super_role_id = RoleRepository.create_role("超级管理员", "super_admin", is_system=1) user_role = RoleRepository.get_by_code("user") user_role_id = user_role["id"] if user_role else RoleRepository.create_role("普通用户", "user", is_system=1) member_role = RoleRepository.get_by_code("member") if not member_role: RoleRepository.create_role("会员用户", "member", is_system=1) # 注入默认菜单 menus = MenuRepository.get_all_menus() if not menus: sys_id = MenuRepository.create_menu("系统管理", "", "layui-icon-set", 0, 1) MenuRepository.create_menu("用户管理", "/user/list", "layui-icon-user", sys_id, 1) MenuRepository.create_menu("角色管理", "/role/list", "layui-icon-group", sys_id, 2) MenuRepository.create_menu("功能管理", "/menu/list", "layui-icon-app", sys_id, 3) # 给超管赋权 all_menus = MenuRepository.get_all_menus() RoleRepository.assign_role_menus(super_role_id, [m['id'] for m in all_menus]) # 注入默认管理员 admin_user = UserRepository.get_user_by_username("admin") if not admin_user: UserRepository.create_user("admin", "admin888", super_role_id) else: # 确保已存在的 admin 绑定了超管角色 with get_connection() as conn: conn.execute("INSERT OR IGNORE INTO user_roles (user_id, role_id) VALUES (?, ?)", (admin_user['id'], super_role_id)) with get_connection() as conn: # 插入默认菜单 conn.execute("INSERT OR IGNORE INTO menus (id, parent_id, name, icon, url, order_num) VALUES (?, ?, ?, ?, ?, ?)", (5, 0, '模型引擎', 'layui-icon-engine', '/model_engine/list', 5)) conn.execute("INSERT OR IGNORE INTO menus (id, parent_id, name, icon, url, order_num) VALUES (?, ?, ?, ?, ?, ?)", (6, 0, '瞭望管理', 'layui-icon-find-fill', '', 6)) conn.execute("INSERT OR IGNORE INTO menus (id, parent_id, name, icon, url, order_num) VALUES (?, ?, ?, ?, ?, ?)", (7, 6, '数据源管理', '', '/spy_source/list', 1)) conn.execute("INSERT OR IGNORE INTO menus (id, parent_id, name, icon, url, order_num) VALUES (?, ?, ?, ?, ?, ?)", (8, 6, '瞭望采集', '', '/spy', 2)) conn.execute("INSERT OR IGNORE INTO menus (id, parent_id, name, icon, url, order_num) VALUES (?, ?, ?, ?, ?, ?)", (9, 0, '数据仓库', 'layui-icon-table', '/data_warehouse/list', 7)) api_parent_row = conn.execute("SELECT id FROM menus WHERE parent_id=0 AND name=?", ("接口服务",)).fetchone() if not api_parent_row: api_parent_row = conn.execute("SELECT id FROM menus WHERE parent_id=0 AND name=?", ("系统管理",)).fetchone() api_parent_id = api_parent_row["id"] if api_parent_row else 0 api_row = conn.execute("SELECT id FROM menus WHERE url=?", ("/api_manage/list",)).fetchone() if not api_row: conn.execute( "INSERT OR IGNORE INTO menus (id, parent_id, name, icon, url, order_num) VALUES (?, ?, ?, ?, ?, ?)", (10, api_parent_id, '接口管理', 'layui-icon-link', '/api_manage/list', 8) ) api_row = conn.execute("SELECT id FROM menus WHERE url=?", ("/api_manage/list",)).fetchone() if not api_row: cursor = conn.execute( "INSERT INTO menus (parent_id, name, icon, url, order_num) VALUES (?, ?, ?, ?, ?)", (api_parent_id, '接口管理', 'layui-icon-link', '/api_manage/list', 8) ) api_menu_id = cursor.lastrowid else: api_menu_id = api_row["id"] api_current = conn.execute("SELECT name, icon FROM menus WHERE id=?", (api_menu_id,)).fetchone() if api_current: if not (api_current["name"] or "").strip(): conn.execute("UPDATE menus SET name=? WHERE id=?", ('接口管理', api_menu_id)) if not (api_current["icon"] or "").strip(): conn.execute("UPDATE menus SET icon=? WHERE id=?", ('layui-icon-link', api_menu_id)) # 确保超级管理员拥有上述菜单权限 for mid in [5, 6, 7, 8, 9, api_menu_id]: conn.execute("INSERT OR IGNORE INTO role_menus (role_id, menu_id) VALUES (?, ?)", (super_role_id, mid)) conn.execute( """ INSERT OR IGNORE INTO api_endpoints (name, url, method, response_format, sample_request, qps_window, qps_max, bypass_token, remark, is_active, update_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, current_timestamp) """, ( "网易云随机音乐", "https://api.52vmy.cn/api/music/wy/rand", "GET", "JSON", "https://api.52vmy.cn/api/music/wy/rand", 2, 4, "", "", 1, ), ) conn.execute( """ INSERT OR IGNORE INTO api_endpoints (name, url, method, response_format, sample_request, qps_window, qps_max, bypass_token, remark, is_active, update_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, current_timestamp) """, ( "三日天气查询", "https://api.52vmy.cn/api/query/tian", "GET", "JSON", "https://api.52vmy.cn/api/query/tian?city=北京市", 2, 4, "", "点击前往三日天气API", 1, ), ) service_row = conn.execute("SELECT id FROM menus WHERE parent_id=0 AND name=?", ("智能服务",)).fetchone() if not service_row: cursor = conn.execute( "INSERT INTO menus (parent_id, name, icon, url, order_num) VALUES (?, ?, ?, ?, ?)", (0, "智能服务", "layui-icon-component", "", 4) ) service_menu_id = cursor.lastrowid else: service_menu_id = service_row["id"] de_row = conn.execute("SELECT id FROM menus WHERE url=?", ("/digital_employee/list",)).fetchone() if not de_row: cursor = conn.execute( "INSERT INTO menus (parent_id, name, icon, url, order_num) VALUES (?, ?, ?, ?, ?)", (service_menu_id, "数字员工", "layui-icon-username", "/digital_employee/list", 1) ) de_menu_id = cursor.lastrowid else: de_menu_id = de_row["id"] conn.execute( "UPDATE menus SET name=?, icon=?, parent_id=?, order_num=? WHERE id=?", ("数字员工", "layui-icon-username", service_menu_id, 1, de_menu_id) ) sess_row = conn.execute("SELECT id FROM menus WHERE url=?", ("/chat_admin/sessions",)).fetchone() if not sess_row: cursor = conn.execute( "INSERT INTO menus (parent_id, name, icon, url, order_num) VALUES (?, ?, ?, ?, ?)", (service_menu_id, "会话管理", "layui-icon-dialogue", "/chat_admin/sessions", 2) ) sess_menu_id = cursor.lastrowid else: sess_menu_id = sess_row["id"] conn.execute( "UPDATE menus SET name=?, icon=?, parent_id=?, order_num=? WHERE id=?", ("会话管理", "layui-icon-dialogue", service_menu_id, 2, sess_menu_id) ) msg_row = conn.execute("SELECT id FROM menus WHERE url=?", ("/chat_admin/messages",)).fetchone() if not msg_row: cursor = conn.execute( "INSERT INTO menus (parent_id, name, icon, url, order_num) VALUES (?, ?, ?, ?, ?)", (service_menu_id, "对话管理", "layui-icon-chat", "/chat_admin/messages", 3) ) msg_menu_id = cursor.lastrowid else: msg_menu_id = msg_row["id"] conn.execute( "UPDATE menus SET name=?, icon=?, parent_id=?, order_num=? WHERE id=?", ("对话管理", "layui-icon-chat", service_menu_id, 3, msg_menu_id) ) skill_row = conn.execute("SELECT id FROM menus WHERE url=?", ("/skill_manage/list",)).fetchone() if not skill_row: cursor = conn.execute( "INSERT INTO menus (parent_id, name, icon, url, order_num) VALUES (?, ?, ?, ?, ?)", (service_menu_id, "技能管理", "layui-icon-template-1", "/skill_manage/list", 4) ) skill_menu_id = cursor.lastrowid else: skill_menu_id = skill_row["id"] conn.execute( "UPDATE menus SET name=?, icon=?, parent_id=?, order_num=? WHERE id=?", ("技能管理", "layui-icon-template-1", service_menu_id, 4, skill_menu_id) ) conn.execute("INSERT OR IGNORE INTO role_menus (role_id, menu_id) VALUES (?, ?)", (super_role_id, service_menu_id)) conn.execute("INSERT OR IGNORE INTO role_menus (role_id, menu_id) VALUES (?, ?)", (super_role_id, de_menu_id)) conn.execute("INSERT OR IGNORE INTO role_menus (role_id, menu_id) VALUES (?, ?)", (super_role_id, sess_menu_id)) conn.execute("INSERT OR IGNORE INTO role_menus (role_id, menu_id) VALUES (?, ?)", (super_role_id, msg_menu_id)) conn.execute("INSERT OR IGNORE INTO role_menus (role_id, menu_id) VALUES (?, ?)", (super_role_id, skill_menu_id)) music_api = conn.execute("SELECT id FROM api_endpoints WHERE url=? AND method='GET'", ("https://api.52vmy.cn/api/music/wy/rand",)).fetchone() weather_api = conn.execute("SELECT id FROM api_endpoints WHERE url=? AND method='GET'", ("https://api.52vmy.cn/api/query/tian",)).fetchone() conn.execute( """ INSERT OR IGNORE INTO digital_employees (name, alias, category, description, ai_prompt, api_endpoint_id, api_param_name, is_active, update_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, current_timestamp) """, ( "川小农", "川小农", "AI", "基于默认模型与 Prompt 的智能对话数字员工", "你是数字员工“川小农”。你以专业、克制、可执行的方式回答用户问题。你必须用中文输出。你会先给出结论,再给出清晰步骤与注意事项。遇到信息不足时,先提出最少的澄清点,然后给出可行的默认方案。", None, "", 1, ), ) if weather_api: conn.execute( """ INSERT OR IGNORE INTO digital_employees (name, alias, category, description, ai_prompt, api_endpoint_id, api_param_name, is_active, update_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, current_timestamp) """, ( "天气", "天气", "NORMAL", "通过接口服务查询天气:@天气 北京市", "", int(weather_api["id"]), "city", 1, ), ) if music_api: conn.execute( """ INSERT OR IGNORE INTO digital_employees (name, alias, category, description, ai_prompt, api_endpoint_id, api_param_name, is_active, update_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, current_timestamp) """, ( "音乐", "音乐", "NORMAL", "随机音乐卡片:@音乐", "", int(music_api["id"]), "", 1, ), ) # 注入默认的百度新闻采集源 baidu_source = conn.execute("SELECT id FROM spy_sources WHERE name='百度新闻'").fetchone() if not baidu_source: headers_str = """Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7 Accept-Encoding: gzip, deflate, br, zstd Accept-Language: zh-CN,zh;q=0.9 Cache-Control: no-cache Connection: keep-alive Cookie: BIDUPSID=5CFCC8D701BF7571598CE9F66EE8E6B5; PSTM=1775407070; BD_UPN=1a314753; BAIDUID=5CFCC8D701BF75717E6B8B51D00D380F:SL=0:NR=10:FG=1; Host: www.baidu.com Pragma: no-cache Referer: https://news.baidu.com/ Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36 QQBrowser/21.1.8743.400""" conn.execute( "INSERT INTO spy_sources (name, entry_url, request_headers, is_active) VALUES (?, ?, ?, ?)", ("百度新闻", "https://www.baidu.com/s?rtt=1&bsst=1&cl=2&tn=news&rsv_dl=ns_pc&word={关键字}&pn={分页步进}", headers_str, 1) ) app = make_app() server = HTTPServer(app) server.bind(10088) # 自动CPU核心数 server.start() try: print("====== Server 启动成功 ======== 端口:10088 ======", flush=True) except OSError: pass tornado.ioloop.IOLoop.current().start()