| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251 |
- import { Hono } from "hono";
- import debug from "debug";
- import type {
- FileLibrary,
- FileCategory,
- KnowInfo,
- ThemeSettings,
- SystemSetting,
- SystemSettingGroupData,
- } from "../client/share/types.ts";
- import {
- EnableStatus,
- DeleteStatus,
- ThemeMode,
- FontSize,
- CompactMode,
- } from "../client/share/types.ts";
- import type { Variables, WithAuth } from "./app.tsx";
- const log = {
- api: debug("api:sys"),
- };
- // 创建知识库管理路由
- export function createKnowInfoRoutes(withAuth: WithAuth) {
- const knowInfoRoutes = new Hono<{ Variables: Variables }>();
- // 获取知识库文章列表
- knowInfoRoutes.get("/", withAuth, async (c) => {
- try {
- const apiClient = c.get('apiClient');
- // 获取分页参数
- const page = Number(c.req.query("page")) || 1;
- const limit = Number(c.req.query("limit")) || 10;
- const offset = (page - 1) * limit;
- // 获取筛选参数
- const title = c.req.query("title");
- const category = c.req.query("category");
- const tags = c.req.query("tags");
- // 构建查询
- let query = apiClient.database
- .table("know_info")
- .where("is_deleted", 0)
- .orderBy("id", "desc");
- // 应用筛选条件
- if (title) {
- query = query.where("title", "like", `%${title}%`);
- }
- if (category) {
- query = query.where("category", category);
- }
- if (tags) {
- query = query.where("tags", "like", `%${tags}%`);
- }
- // 克隆查询以获取总数
- const countQuery = query.clone();
- // 执行分页查询
- const articles = await query.limit(limit).offset(offset);
- // 获取总数
- const count = await countQuery.count();
- return c.json({
- data: articles,
- pagination: {
- total: Number(count),
- current: page,
- pageSize: limit,
- totalPages: Math.ceil(Number(count) / limit),
- },
- });
- } catch (error) {
- log.api("获取知识库文章列表失败:", error);
- return c.json({ error: "获取知识库文章列表失败" }, 500);
- }
- });
- // 获取单个知识库文章
- knowInfoRoutes.get("/:id", withAuth, async (c) => {
- try {
- const id = Number(c.req.param("id"));
- if (!id || isNaN(id)) {
- return c.json({ error: "无效的文章ID" }, 400);
- }
- const apiClient = c.get('apiClient');
- const [article] = await apiClient.database
- .table("know_info")
- .where({ id, is_deleted: 0 });
- if (!article) {
- return c.json({ error: "文章不存在" }, 404);
- }
- return c.json({message: '获取知识库文章详情成功', data: article});
- } catch (error) {
- log.api("获取知识库文章详情失败:", error);
- return c.json({ error: "获取知识库文章详情失败" }, 500);
- }
- });
- // 创建知识库文章
- knowInfoRoutes.post("/", withAuth, async (c) => {
- try {
- const articleData = (await c.req.json()) as Partial<KnowInfo>;
- // 验证必填字段
- if (!articleData.title) {
- return c.json({ error: "文章标题不能为空" }, 400);
- }
- // 如果作者为空,则使用当前用户的用户名
- if (!articleData.author) {
- const user = c.get("user");
- articleData.author = user ? user.username : "unknown";
- }
- const apiClient = c.get('apiClient');
- const [id] = await apiClient.database
- .table("know_info")
- .insert(articleData);
- // 获取创建的文章
- const [createdArticle] = await apiClient.database
- .table("know_info")
- .where("id", id);
- return c.json({
- message: "知识库文章创建成功",
- data: createdArticle,
- });
- } catch (error) {
- log.api("创建知识库文章失败:", error);
- return c.json({ error: "创建知识库文章失败" }, 500);
- }
- });
- // 更新知识库文章
- knowInfoRoutes.put("/:id", withAuth, async (c) => {
- try {
- const id = Number(c.req.param("id"));
- if (!id || isNaN(id)) {
- return c.json({ error: "无效的文章ID" }, 400);
- }
- const articleData = (await c.req.json()) as Partial<KnowInfo>;
- // 验证必填字段
- if (!articleData.title) {
- return c.json({ error: "文章标题不能为空" }, 400);
- }
- const apiClient = c.get('apiClient');
- // 检查文章是否存在
- const [existingArticle] = await apiClient.database
- .table("know_info")
- .where({ id, is_deleted: 0 });
- if (!existingArticle) {
- return c.json({ error: "文章不存在" }, 404);
- }
- // 更新文章
- await apiClient.database
- .table("know_info")
- .where("id", id)
- .update({
- ...articleData,
- updated_at: apiClient.database.fn.now(),
- });
- // 获取更新后的文章
- const [updatedArticle] = await apiClient.database
- .table("know_info")
- .where("id", id);
- return c.json({
- message: "知识库文章更新成功",
- data: updatedArticle,
- });
- } catch (error) {
- log.api("更新知识库文章失败:", error);
- return c.json({ error: "更新知识库文章失败" }, 500);
- }
- });
- // 删除知识库文章(软删除)
- knowInfoRoutes.delete("/:id", withAuth, async (c) => {
- try {
- const id = Number(c.req.param("id"));
- if (!id || isNaN(id)) {
- return c.json({ error: "无效的文章ID" }, 400);
- }
- const apiClient = c.get('apiClient');
- // 检查文章是否存在
- const [existingArticle] = await apiClient.database
- .table("know_info")
- .where({ id, is_deleted: 0 });
- if (!existingArticle) {
- return c.json({ error: "文章不存在" }, 404);
- }
- // 软删除文章
- await apiClient.database.table("know_info").where("id", id).update({
- is_deleted: 1,
- updated_at: apiClient.database.fn.now(),
- });
- return c.json({
- message: "知识库文章删除成功",
- });
- } catch (error) {
- log.api("删除知识库文章失败:", error);
- return c.json({ error: "删除知识库文章失败" }, 500);
- }
- });
-
- return knowInfoRoutes;
- }
- // 创建文件分类路由
- export function createFileCategoryRoutes(withAuth: WithAuth) {
- const fileCategoryRoutes = new Hono<{ Variables: Variables }>();
- // 获取文件分类列表
- fileCategoryRoutes.get("/", withAuth, async (c) => {
- try {
- const apiClient = c.get('apiClient');
- const page = Number(c.req.query("page")) || 1;
- const pageSize = Number(c.req.query("pageSize")) || 10;
- const offset = (page - 1) * pageSize;
- const search = c.req.query("search") || "";
- let query = apiClient.database.table("file_category").orderBy("id", "desc");
- if (search) {
- query = query.where("name", "like", `%${search}%`);
- }
- const total = await query.clone().count();
- const categories = await query
- .select("id", "name", "code", "description")
- .limit(pageSize)
- .offset(offset);
- return c.json({
- data: categories,
- total: Number(total),
- page,
- pageSize,
- });
- } catch (error) {
- log.api("获取文件分类列表失败:", error);
- return c.json({ error: "获取文件分类列表失败" }, 500);
- }
- });
- // 创建文件分类
- fileCategoryRoutes.post("/", withAuth, async (c) => {
- try {
- const apiClient = c.get('apiClient');
- const data = (await c.req.json()) as Partial<FileCategory>;
- // 验证必填字段
- if (!data.name) {
- return c.json({ error: "分类名称不能为空" }, 400);
- }
- // 插入文件分类
- const [id] = await apiClient.database.table("file_category").insert({
- ...data,
- created_at: apiClient.database.fn.now(),
- updated_at: apiClient.database.fn.now(),
- });
- return c.json({
- message: "文件分类创建成功",
- data: {
- id,
- ...data,
- },
- });
- } catch (error) {
- log.api("创建文件分类失败:", error);
- return c.json({ error: "创建文件分类失败" }, 500);
- }
- });
- // 更新文件分类
- fileCategoryRoutes.put("/:id", withAuth, async (c) => {
- try {
- const apiClient = c.get('apiClient');
- const id = Number(c.req.param("id"));
- if (!id || isNaN(id)) {
- return c.json({ error: "无效的分类ID" }, 400);
- }
- const data = (await c.req.json()) as Partial<FileCategory>;
- // 更新文件分类
- await apiClient.database
- .table("file_category")
- .where("id", id)
- .update({
- ...data,
- updated_at: apiClient.database.fn.now(),
- });
- return c.json({
- message: "文件分类更新成功",
- data: {
- id,
- ...data,
- },
- });
- } catch (error) {
- log.api("更新文件分类失败:", error);
- return c.json({ error: "更新文件分类失败" }, 500);
- }
- });
- // 删除文件分类
- fileCategoryRoutes.delete("/:id", withAuth, async (c) => {
- try {
- const apiClient = c.get('apiClient');
- const id = Number(c.req.param("id"));
- if (!id || isNaN(id)) {
- return c.json({ error: "无效的分类ID" }, 400);
- }
- await apiClient.database.table("file_category").where("id", id).update({
- is_deleted: DeleteStatus.DELETED,
- updated_at: apiClient.database.fn.now(),
- });
- return c.json({
- message: "文件分类删除成功",
- });
- } catch (error) {
- log.api("删除文件分类失败:", error);
- return c.json({ error: "删除文件分类失败" }, 500);
- }
- });
-
- return fileCategoryRoutes;
- }
- // 创建文件上传路由
- export function createFileUploadRoutes(withAuth: WithAuth) {
- const fileUploadRoutes = new Hono<{ Variables: Variables }>();
- // 获取 MinIO 上传策略
- fileUploadRoutes.get("/policy", withAuth, async (c) => {
- try {
- const prefix = c.req.query("prefix") || "uploads/";
- const filename = c.req.query("filename");
- const maxSize = Number(c.req.query("maxSize")) || 10 * 1024 * 1024; // 默认10MB
- if (!filename) {
- return c.json({ error: "文件名不能为空" }, 400);
- }
- const apiClient = c.get('apiClient');
- const policy = await apiClient.storage.getUploadPolicy(
- prefix,
- filename,
- maxSize
- );
- return c.json({
- message: "获取上传策略成功",
- data: policy,
- });
- } catch (error) {
- log.api("获取上传策略失败:", error);
- return c.json({ error: "获取上传策略失败" }, 500);
- }
- });
- // 保存文件信息到文件库
- fileUploadRoutes.post("/save", withAuth, async (c) => {
- try {
- const fileData = (await c.req.json()) as Partial<FileLibrary>;
- const user = c.get("user");
- // 验证必填字段
- if (!fileData.file_name || !fileData.file_path || !fileData.file_type) {
- return c.json({ error: "文件名、路径和类型不能为空" }, 400);
- }
- const apiClient = c.get('apiClient');
- // 设置上传者信息
- if (user) {
- fileData.uploader_id = user.id;
- fileData.uploader_name = user.nickname || user.username;
- }
- // 插入文件库记录
- const [id] = await apiClient.database.table("file_library").insert({
- ...fileData,
- download_count: 0,
- is_disabled: EnableStatus.ENABLED,
- is_deleted: DeleteStatus.NOT_DELETED,
- created_at: apiClient.database.fn.now(),
- updated_at: apiClient.database.fn.now(),
- });
- // 获取插入的数据
- const [insertedFile] = await apiClient.database
- .table("file_library")
- .where("id", id);
- return c.json({
- message: "文件信息保存成功",
- data: insertedFile,
- });
- } catch (error) {
- log.api("保存文件信息失败:", error);
- return c.json({ error: "保存文件信息失败" }, 500);
- }
- });
- // 获取文件列表
- fileUploadRoutes.get("/list", withAuth, async (c) => {
- try {
- const page = Number(c.req.query("page")) || 1;
- const pageSize = Number(c.req.query("pageSize")) || 10;
- const category_id = c.req.query("category_id");
- const fileType = c.req.query("fileType");
- const keyword = c.req.query("keyword");
- const apiClient = c.get('apiClient');
- let query = apiClient.database
- .table("file_library")
- .where("is_deleted", DeleteStatus.NOT_DELETED)
- .orderBy("created_at", "desc");
- // 应用过滤条件
- if (category_id) {
- query = query.where("category_id", category_id);
- }
- if (fileType) {
- query = query.where("file_type", fileType);
- }
- if (keyword) {
- query = query.where((builder) => {
- builder
- .where("file_name", "like", `%${keyword}%`)
- .orWhere("description", "like", `%${keyword}%`)
- .orWhere("tags", "like", `%${keyword}%`);
- });
- }
- // 获取总数
- const total = await query.clone().count();
- // 分页查询
- const files = await query.limit(pageSize).offset((page - 1) * pageSize);
- return c.json({
- message: "获取文件列表成功",
- data: {
- list: files,
- pagination: {
- current: page,
- pageSize,
- total: Number(total),
- },
- },
- });
- } catch (error) {
- log.api("获取文件列表失败:", error);
- return c.json({ error: "获取文件列表失败" }, 500);
- }
- });
- // 获取单个文件信息
- fileUploadRoutes.get("/:id", withAuth, async (c) => {
- try {
- const id = Number(c.req.param("id"));
- if (!id || isNaN(id)) {
- return c.json({ error: "无效的文件ID" }, 400);
- }
- const apiClient = c.get('apiClient');
- const file = await apiClient.database
- .table("file_library")
- .where("id", id)
- .where("is_deleted", DeleteStatus.NOT_DELETED)
- .first();
- if (!file) {
- return c.json({ error: "文件不存在" }, 404);
- }
- return c.json({
- message: "获取文件信息成功",
- data: file,
- });
- } catch (error) {
- log.api("获取文件信息失败:", error);
- return c.json({ error: "获取文件信息失败" }, 500);
- }
- });
- // 增加文件下载计数
- fileUploadRoutes.post("/:id/download", withAuth, async (c) => {
- try {
- const id = Number(c.req.param("id"));
- if (!id || isNaN(id)) {
- return c.json({ error: "无效的文件ID" }, 400);
- }
- const apiClient = c.get('apiClient');
- // 查询文件是否存在
- const file = await apiClient.database
- .table("file_library")
- .where("id", id)
- .where("is_deleted", DeleteStatus.NOT_DELETED)
- .first();
- if (!file) {
- return c.json({ error: "文件不存在" }, 404);
- }
- // 增加下载计数
- await apiClient.database
- .table("file_library")
- .where("id", id)
- .update({
- download_count: apiClient.database.raw("download_count + 1"),
- updated_at: apiClient.database.fn.now(),
- });
- return c.json({
- message: "更新下载计数成功",
- });
- } catch (error) {
- log.api("更新下载计数失败:", error);
- return c.json({ error: "更新下载计数失败" }, 500);
- }
- });
- // 删除文件
- fileUploadRoutes.delete("/:id", withAuth, async (c) => {
- try {
- const id = Number(c.req.param("id"));
- if (!id || isNaN(id)) {
- return c.json({ error: "无效的文件ID" }, 400);
- }
- const apiClient = c.get('apiClient');
- // 查询文件是否存在
- const file = await apiClient.database
- .table("file_library")
- .where("id", id)
- .where("is_deleted", DeleteStatus.NOT_DELETED)
- .first();
- if (!file) {
- return c.json({ error: "文件不存在" }, 404);
- }
- // 软删除文件
- await apiClient.database.table("file_library").where("id", id).update({
- is_deleted: DeleteStatus.DELETED,
- updated_at: apiClient.database.fn.now(),
- });
- return c.json({
- message: "文件删除成功",
- });
- } catch (error) {
- log.api("删除文件失败:", error);
- return c.json({ error: "删除文件失败" }, 500);
- }
- });
-
- return fileUploadRoutes;
- }
- // 创建主题设置路由
- export function createThemeRoutes(withAuth: WithAuth) {
- const themeRoutes = new Hono<{ Variables: Variables }>();
- // 获取当前主题设置
- themeRoutes.get("/", withAuth, async (c) => {
- try {
- const apiClient = c.get('apiClient');
- const user = c.get('user');
- if (!user) {
- return c.json({ error: "未授权访问" }, 401);
- }
- // 获取用户的主题设置
- let themeSettings = await apiClient.database
- .table("theme_settings")
- .where("user_id", user.id)
- .first();
- // 如果用户没有主题设置,则创建默认设置
- if (!themeSettings) {
- const defaultSettings = {
- theme_mode: ThemeMode.LIGHT,
- primary_color: '#1890ff',
- font_size: FontSize.MEDIUM,
- is_compact: CompactMode.NORMAL
- };
- const [id] = await apiClient.database.table("theme_settings").insert({
- user_id: user.id,
- settings: defaultSettings,
- created_at: apiClient.database.fn.now(),
- updated_at: apiClient.database.fn.now(),
- });
- themeSettings = await apiClient.database
- .table("theme_settings")
- .where("id", id)
- .first();
- }
- return c.json({
- message: "获取主题设置成功",
- data: themeSettings?.settings,
- });
- } catch (error) {
- log.api("获取主题设置失败:", error);
- return c.json({ error: "获取主题设置失败" }, 500);
- }
- });
- // 更新主题设置
- themeRoutes.put("/", withAuth, async (c) => {
- try {
- const user = c.get('user');
- const apiClient = c.get('apiClient');
- if (!user) {
- return c.json({ error: "未授权访问" }, 401);
- }
- const themeData = (await c.req.json()) as Partial<ThemeSettings>;
- // 检查用户是否已有主题设置
- const existingTheme = await apiClient.database
- .table("theme_settings")
- .where("user_id", user.id)
- .first();
- if (existingTheme) {
- // 更新现有设置
- const currentSettings = existingTheme.settings || {};
- const updatedSettings = {
- ...currentSettings,
- ...themeData
- };
- await apiClient.database
- .table("theme_settings")
- .where("user_id", user.id)
- .update({
- settings: JSON.stringify(updatedSettings),
- updated_at: apiClient.database.fn.now(),
- });
- } else {
- // 创建新设置
- const defaultSettings = {
- theme_mode: ThemeMode.LIGHT,
- primary_color: '#1890ff',
- font_size: FontSize.MEDIUM,
- is_compact: CompactMode.NORMAL
- };
- const updatedSettings = {
- ...defaultSettings,
- ...themeData
- };
- await apiClient.database.table("theme_settings").insert({
- user_id: user.id,
- settings: updatedSettings,
- created_at: apiClient.database.fn.now(),
- updated_at: apiClient.database.fn.now(),
- });
- }
- // 获取更新后的主题设置
- const updatedTheme = await apiClient.database
- .table("theme_settings")
- .where("user_id", user.id)
- .first();
- return c.json({
- message: "主题设置更新成功",
- data: updatedTheme,
- });
- } catch (error) {
- log.api("更新主题设置失败:", error);
- return c.json({ error: "更新主题设置失败" }, 500);
- }
- });
- // 重置主题设置为默认值
- themeRoutes.post("/reset", withAuth, async (c) => {
- try {
- const user = c.get('user');
- const apiClient = c.get('apiClient');
- if (!user) {
- return c.json({ error: "未授权访问" }, 401);
- }
- // 默认主题设置
- const defaultSettings = {
- theme_mode: ThemeMode.LIGHT,
- primary_color: '#1890ff',
- font_size: FontSize.MEDIUM,
- is_compact: CompactMode.NORMAL
- };
- // 更新用户的主题设置
- await apiClient.database
- .table("theme_settings")
- .where("user_id", user.id)
- .update({
- settings: JSON.stringify(defaultSettings),
- updated_at: apiClient.database.fn.now(),
- });
- // 获取更新后的主题设置
- const updatedTheme = await apiClient.database
- .table("theme_settings")
- .where("user_id", user.id)
- .first();
- return c.json({
- message: "主题设置已重置为默认值",
- data: updatedTheme,
- });
- } catch (error) {
- log.api("重置主题设置失败:", error);
- return c.json({ error: "重置主题设置失败" }, 500);
- }
- });
- return themeRoutes;
- }
- // 创建系统设置路由
- export function createSystemSettingsRoutes(withAuth: WithAuth) {
- const settingsRoutes = new Hono<{ Variables: Variables }>();
- // 获取所有系统设置(按分组)
- settingsRoutes.get('/', withAuth, async (c) => {
- try {
- const apiClient = c.get('apiClient');
- const settings = await apiClient.database
- .table('system_settings')
- .select('*');
- // 按分组整理数据
- const groupedSettings = settings.reduce((acc: SystemSettingGroupData[], setting) => {
- const groupIndex = acc.findIndex((g: SystemSettingGroupData) => g.name === setting.group);
- if (groupIndex === -1) {
- acc.push({
- name: setting.group,
- description: `${setting.group}组设置`,
- settings: [{
- id: setting.id,
- key: setting.key,
- value: setting.value,
- description: setting.description,
- group: setting.group
- }]
- });
- } else {
- acc[groupIndex].settings.push({
- id: setting.id,
- key: setting.key,
- value: setting.value,
- description: setting.description,
- group: setting.group
- });
- }
- return acc;
- }, []);
- return c.json({
- message: '获取系统设置成功',
- data: groupedSettings
- });
- } catch (error) {
- log.api('获取系统设置失败:', error);
- return c.json({ error: '获取系统设置失败' }, 500);
- }
- });
- // 获取指定分组的系统设置
- settingsRoutes.get('/group/:group', withAuth, async (c) => {
- try {
- const apiClient = c.get('apiClient');
- const group = c.req.param('group');
-
- const settings = await apiClient.database
- .table('system_settings')
- .where('group', group)
- .select('*');
- return c.json({
- message: '获取分组设置成功',
- data: settings
- });
- } catch (error) {
- log.api('获取分组设置失败:', error);
- return c.json({ error: '获取分组设置失败' }, 500);
- }
- });
- // 更新系统设置
- settingsRoutes.put('/:key', withAuth, async (c) => {
- try {
- const apiClient = c.get('apiClient');
- const key = c.req.param('key');
- const settingData = await c.req.json();
- // 验证设置是否存在
- const existingSetting = await apiClient.database
- .table('system_settings')
- .where('key', key)
- .first();
- if (!existingSetting) {
- return c.json({ error: '设置项不存在' }, 404);
- }
- // 更新设置
- await apiClient.database
- .table('system_settings')
- .where('key', key)
- .update({
- value: settingData.value,
- updated_at: apiClient.database.fn.now()
- });
- // 获取更新后的设置
- const updatedSetting = await apiClient.database
- .table('system_settings')
- .where('key', key)
- .first();
- return c.json({
- message: '系统设置已更新',
- data: updatedSetting
- });
- } catch (error) {
- log.api('更新系统设置失败:', error);
- return c.json({ error: '更新系统设置失败' }, 500);
- }
- });
- // 批量更新系统设置
- settingsRoutes.put('/', withAuth, async (c) => {
- try {
- const apiClient = c.get('apiClient');
- const settingsData = await c.req.json();
- // 验证数据格式
- if (!Array.isArray(settingsData)) {
- return c.json({ error: '无效的请求数据格式,应为数组' }, 400);
- }
- const trxProvider = apiClient.database.transactionProvider();
- const trx = await trxProvider();
- for (const setting of settingsData) {
- if (!setting.key) continue;
- // 验证设置是否存在
- const existingSetting = await trx.table('system_settings')
- .where('key', setting.key)
- .first();
- if (!existingSetting) {
- throw new Error(`设置项 ${setting.key} 不存在`);
- }
- // 更新设置
- await trx.table('system_settings')
- .where('key', setting.key)
- .update({
- value: setting.value,
- updated_at: trx.fn.now()
- });
- }
- await trx.commit();
- // 获取所有更新后的设置
- const updatedSettings = await apiClient.database
- .table('system_settings')
- .whereIn('key', settingsData.map(s => s.key))
- .select('*');
- return c.json({
- message: '系统设置已批量更新',
- data: updatedSettings
- });
- } catch (error) {
- log.api('批量更新系统设置失败:', error);
- return c.json({ error: '批量更新系统设置失败' }, 500);
- }
- });
- // 重置系统设置
- settingsRoutes.post('/reset', withAuth, async (c) => {
- try {
- const apiClient = c.get('apiClient');
- // 重置为迁移文件中定义的初始值
- const trxProvider = apiClient.database.transactionProvider();
- const trx = await trxProvider();
- // 清空现有设置
- await trx.table('system_settings').delete();
-
- // 插入默认设置
- await trx.table('system_settings').insert([
- // 基础设置组
- {
- key: 'SITE_NAME',
- value: '应用管理系统',
- description: '站点名称',
- group: 'basic',
- created_at: apiClient.database.fn.now(),
- updated_at: apiClient.database.fn.now()
- },
- {
- key: 'SITE_DESCRIPTION',
- value: '一个强大的应用管理系统',
- description: '站点描述',
- group: 'basic',
- created_at: apiClient.database.fn.now(),
- updated_at: apiClient.database.fn.now()
- },
- {
- key: 'SITE_KEYWORDS',
- value: '应用管理,系统管理,后台管理',
- description: '站点关键词',
- group: 'basic',
- created_at: apiClient.database.fn.now(),
- updated_at: apiClient.database.fn.now()
- },
- // 功能设置组
- {
- key: 'ENABLE_REGISTER',
- value: 'true',
- description: '是否开启注册',
- group: 'feature',
- created_at: apiClient.database.fn.now(),
- updated_at: apiClient.database.fn.now()
- },
- {
- key: 'ENABLE_CAPTCHA',
- value: 'true',
- description: '是否开启验证码',
- group: 'feature',
- created_at: apiClient.database.fn.now(),
- updated_at: apiClient.database.fn.now()
- },
- {
- key: 'LOGIN_ATTEMPTS',
- value: '5',
- description: '登录尝试次数',
- group: 'feature',
- created_at: apiClient.database.fn.now(),
- updated_at: apiClient.database.fn.now()
- },
- // 上传设置组
- {
- key: 'UPLOAD_MAX_SIZE',
- value: '10',
- description: '最大上传大小(MB)',
- group: 'upload',
- created_at: apiClient.database.fn.now(),
- updated_at: apiClient.database.fn.now()
- },
- {
- key: 'ALLOWED_FILE_TYPES',
- value: 'jpg,jpeg,png,gif,doc,docx,xls,xlsx,pdf',
- description: '允许的文件类型',
- group: 'upload',
- created_at: apiClient.database.fn.now(),
- updated_at: apiClient.database.fn.now()
- },
- // 通知设置组
- {
- key: 'NOTIFY_ON_LOGIN',
- value: 'true',
- description: '登录通知',
- group: 'notify',
- created_at: apiClient.database.fn.now(),
- updated_at: apiClient.database.fn.now()
- },
- {
- key: 'NOTIFY_ON_ERROR',
- value: 'true',
- description: '错误通知',
- group: 'notify',
- created_at: apiClient.database.fn.now(),
- updated_at: apiClient.database.fn.now()
- }
- ]);
- await trx.commit();
- const resetSettings = await apiClient.database
- .table('system_settings')
- .select('*');
- return c.json({
- message: '系统设置已重置',
- data: resetSettings
- });
- } catch (error) {
- log.api('重置系统设置失败:', error);
- return c.json({ error: '重置系统设置失败' }, 500);
- }
- });
- return settingsRoutes;
- }
- import { Organization } from "../client/share/types.ts";
- // 创建机构管理路由
- export function createOrganizationRoutes(withAuth: WithAuth) {
- const organizationRoutes = new Hono<{ Variables: Variables }>();
- // 获取机构树形列表
- organizationRoutes.get("/tree", withAuth, async (c) => {
- try {
- const apiClient = c.get('apiClient');
- // 获取所有机构数据
- const organizations = await apiClient.database
- .table("organizations")
- .where("is_deleted", DeleteStatus.NOT_DELETED)
- .orderBy("id", "asc");
- // 构建树形结构
- const buildTree = (parentId?: number): Organization[] => {
- return organizations
- .filter((org: any) => org.parent_id === parentId)
- .map((org: any): Organization => ({
- id: org.id,
- name: org.name,
- code: org.code,
- parentId: org.parent_id,
- description: org.description,
- children: buildTree(org.id)
- }));
- };
- const treeData = buildTree();
- return c.json({
- message: "获取机构树成功",
- data: treeData
- });
- } catch (error) {
- log.api("获取机构树失败:", error);
- return c.json({ error: "获取机构树失败" }, 500);
- }
- });
- // 创建新机构
- organizationRoutes.post("/", withAuth, async (c) => {
- try {
- const apiClient = c.get('apiClient');
- const user = c.get('user');
- const orgData = (await c.req.json()) as Partial<Organization>;
- // 验证必填字段
- if (!orgData.name) {
- return c.json({ error: "机构名称不能为空" }, 400);
- }
- if (!orgData.code) {
- return c.json({ error: "机构编码不能为空" }, 400);
- }
- // 插入新机构
- const [id] = await apiClient.database.table("organizations").insert({
- ...orgData,
- created_by: user?.id,
- updated_by: user?.id,
- is_deleted: DeleteStatus.NOT_DELETED,
- created_at: apiClient.database.fn.now(),
- updated_at: apiClient.database.fn.now()
- });
- // 获取创建的机构
- const [createdOrg] = await apiClient.database
- .table("organizations")
- .where("id", id);
- return c.json({
- message: "机构创建成功",
- data: createdOrg
- });
- } catch (error) {
- log.api("创建机构失败:", error);
- return c.json({ error: "创建机构失败" }, 500);
- }
- });
- // 更新机构信息
- organizationRoutes.put("/:id", withAuth, async (c) => {
- try {
- const apiClient = c.get('apiClient');
- const user = c.get('user');
- const id = Number(c.req.param("id"));
- const orgData = (await c.req.json()) as Partial<Organization>;
- if (!id || isNaN(id)) {
- return c.json({ error: "无效的机构ID" }, 400);
- }
- // 验证必填字段
- if (!orgData.name) {
- return c.json({ error: "机构名称不能为空" }, 400);
- }
- // 检查机构是否存在
- const [existingOrg] = await apiClient.database
- .table("organizations")
- .where({ id, is_deleted: DeleteStatus.NOT_DELETED });
- if (!existingOrg) {
- return c.json({ error: "机构不存在" }, 404);
- }
- // 更新机构信息
- await apiClient.database
- .table("organizations")
- .where("id", id)
- .update({
- ...orgData,
- updated_by: user?.id,
- updated_at: apiClient.database.fn.now()
- });
- // 获取更新后的机构
- const [updatedOrg] = await apiClient.database
- .table("organizations")
- .where("id", id);
- return c.json({
- message: "机构更新成功",
- data: updatedOrg
- });
- } catch (error) {
- log.api("更新机构失败:", error);
- return c.json({ error: "更新机构失败" }, 500);
- }
- });
- // 删除机构(软删除)
- organizationRoutes.delete("/:id", withAuth, async (c) => {
- try {
- const apiClient = c.get('apiClient');
- const user = c.get('user');
- const id = Number(c.req.param("id"));
- if (!id || isNaN(id)) {
- return c.json({ error: "无效的机构ID" }, 400);
- }
- // 检查机构是否存在
- const [existingOrg] = await apiClient.database
- .table("organizations")
- .where({ id, is_deleted: DeleteStatus.NOT_DELETED });
- if (!existingOrg) {
- return c.json({ error: "机构不存在" }, 404);
- }
- // 检查是否有子机构
- const [hasChildren] = await apiClient.database
- .table("organizations")
- .where({ parent_id: id, is_deleted: DeleteStatus.NOT_DELETED })
- .limit(1);
- if (hasChildren) {
- return c.json({ error: "该机构下有子机构,不能删除" }, 400);
- }
- // 软删除机构
- await apiClient.database
- .table("organizations")
- .where("id", id)
- .update({
- is_deleted: DeleteStatus.DELETED,
- updated_by: user?.id,
- updated_at: apiClient.database.fn.now()
- });
- return c.json({
- message: "机构删除成功"
- });
- } catch (error) {
- log.api("删除机构失败:", error);
- return c.json({ error: "删除机构失败" }, 500);
- }
- });
- return organizationRoutes;
- }
|