From 7d0a921805b151f0b7d74597c10092f3c22f917f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E6=88=90?= Date: Wed, 25 Mar 2026 19:01:28 +0800 Subject: [PATCH] 1 --- .vscode/launch.json | 13 +++ _docs/sql/001_biz_schema.sql | 13 +-- _docs/sql/002_subscription_menu_seed.sql | 35 +++---- _docs/sql/003_home_menu_seed.sql | 29 ++++++ .../sql/004_migrate_biz_users_to_biz_user.sql | 36 ++++++++ admin/config/README.md | 14 +-- admin/config/index.js | 4 +- admin/src/router/component-map.js | 4 + admin/src/views/home/index.vue | 53 +++++++++++ admin/src/views/subscription/audit_log.vue | 85 ++++++++++------- admin/src/views/subscription/dashboard.vue | 46 +++++++--- admin/src/views/subscription/payment.vue | 4 +- admin/src/views/subscription/plans.vue | 91 ++++++++++++------- .../src/views/subscription/subscriptions.vue | 81 +++++++++++------ admin/src/views/subscription/tokens.vue | 82 +++++++++++------ admin/src/views/subscription/usage.vue | 83 +++++++++++------ admin/src/views/subscription/users.vue | 88 ++++++++++++------ api/model/biz_api_token.js | 4 +- api/model/biz_audit_log.js | 9 +- api/model/biz_plan.js | 4 +- api/model/biz_subscription.js | 4 +- api/model/biz_usage_monthly.js | 4 +- api/model/biz_user.js | 9 +- config/config.js | 4 +- config/framework.config.js | 2 +- framework/node-core-framework.js | 2 +- package.json | 2 +- 27 files changed, 560 insertions(+), 245 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 _docs/sql/003_home_menu_seed.sql create mode 100644 _docs/sql/004_migrate_biz_users_to_biz_user.sql create mode 100644 admin/src/views/home/index.vue diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..c9ec9aa --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,13 @@ +{ + "configurations": [ + { + "name": "Launch Program", + "program": "${workspaceFolder}/app.js", + "request": "launch", + "skipFiles": [ + "/**" + ], + "type": "node" + } + ] +} \ No newline at end of file diff --git a/_docs/sql/001_biz_schema.sql b/_docs/sql/001_biz_schema.sql index 91e3042..a3c8f25 100644 --- a/_docs/sql/001_biz_schema.sql +++ b/_docs/sql/001_biz_schema.sql @@ -1,10 +1,11 @@ -- WechatAdminWeb 订阅模块业务表(MySQL 8+) +-- 业务用户物理表名:biz_user(与 Sequelize 模型 biz_user、freezeTableName 一致) -- 执行前请确认库名;与 api/model/biz_*.js 字段一致 SET NAMES utf8mb4; -- 业务用户(与 sys_user 后台账号区分) -CREATE TABLE IF NOT EXISTS `biz_users` ( +CREATE TABLE IF NOT EXISTS `biz_user` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `name` VARCHAR(100) NOT NULL DEFAULT '', `mobile` VARCHAR(20) NULL DEFAULT NULL, @@ -14,8 +15,8 @@ CREATE TABLE IF NOT EXISTS `biz_users` ( `created_at` DATETIME NOT NULL, `updated_at` DATETIME NOT NULL, PRIMARY KEY (`id`), - KEY `idx_biz_users_mobile` (`mobile`), - KEY `idx_biz_users_status` (`status`) + KEY `idx_biz_user_mobile` (`mobile`), + KEY `idx_biz_user_status` (`status`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='业务用户'; -- 套餐 @@ -56,7 +57,7 @@ CREATE TABLE IF NOT EXISTS `biz_subscriptions` ( KEY `idx_biz_sub_user` (`user_id`), KEY `idx_biz_sub_plan` (`plan_id`), KEY `idx_biz_sub_status_end` (`status`, `end_time`), - CONSTRAINT `fk_biz_sub_user` FOREIGN KEY (`user_id`) REFERENCES `biz_users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `fk_biz_sub_user` FOREIGN KEY (`user_id`) REFERENCES `biz_user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `fk_biz_sub_plan` FOREIGN KEY (`plan_id`) REFERENCES `biz_plans` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='订阅'; @@ -76,7 +77,7 @@ CREATE TABLE IF NOT EXISTS `biz_api_tokens` ( UNIQUE KEY `uk_biz_token_hash` (`token_hash`), KEY `idx_biz_token_user` (`user_id`), KEY `idx_biz_token_plan` (`plan_id`), - CONSTRAINT `fk_biz_token_user` FOREIGN KEY (`user_id`) REFERENCES `biz_users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `fk_biz_token_user` FOREIGN KEY (`user_id`) REFERENCES `biz_user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `fk_biz_token_plan` FOREIGN KEY (`plan_id`) REFERENCES `biz_plans` (`id`) ON DELETE SET NULL ON UPDATE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='API Token'; @@ -96,6 +97,6 @@ CREATE TABLE IF NOT EXISTS `biz_usage_monthly` ( PRIMARY KEY (`id`), UNIQUE KEY `uk_biz_usage_user_month` (`user_id`, `stat_month`), KEY `idx_biz_usage_plan` (`plan_id`), - CONSTRAINT `fk_biz_usage_user` FOREIGN KEY (`user_id`) REFERENCES `biz_users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `fk_biz_usage_user` FOREIGN KEY (`user_id`) REFERENCES `biz_user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `fk_biz_usage_plan` FOREIGN KEY (`plan_id`) REFERENCES `biz_plans` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='月用量'; diff --git a/_docs/sql/002_subscription_menu_seed.sql b/_docs/sql/002_subscription_menu_seed.sql index 0fe341e..8a87651 100644 --- a/_docs/sql/002_subscription_menu_seed.sql +++ b/_docs/sql/002_subscription_menu_seed.sql @@ -1,5 +1,6 @@ -- ============================================================================= -- sys_menu 订阅模块菜单插入脚本(字段与 api/model/sys_menu.js 一致) +-- path:不含斜杠(与首页 path=home 等约定一致);component 仍为 subscription/xxx 供前端映射 -- 执行前请备份。若已存在同名「订阅管理」父菜单,请先删除子菜单再删父级,或改下面名称。 -- 若数据库表另有 created_at / updated_at 等列,请在 INSERT 中补全或给默认值。 -- ============================================================================= @@ -15,7 +16,7 @@ VALUES '订阅管理', 0, 'ios-apps', - '/subscription', + 'subscription', '菜单', 0, 0, @@ -31,14 +32,14 @@ SET @sub_parent_id = LAST_INSERT_ID(); INSERT INTO sys_menu (name, parent_id, icon, path, type, model_id, form_id, component, api_path, is_show_menu, is_show, sort) VALUES - ('运营看板', @sub_parent_id, 'ios-speedometer', '/subscription/dashboard', '页面', 0, 0, 'subscription/dashboard', '', 1, 1, 10), - ('业务用户', @sub_parent_id, 'ios-people', '/subscription/user', '页面', 0, 0, 'subscription/user', '', 1, 1, 20), - ('套餐管理', @sub_parent_id, 'ios-pricetags', '/subscription/plan', '页面', 0, 0, 'subscription/plan', '', 1, 1, 30), - ('订阅列表', @sub_parent_id, 'ios-list', '/subscription/subscription', '页面', 0, 0, 'subscription/subscription', '', 1, 1, 40), - ('API Token', @sub_parent_id, 'ios-key', '/subscription/token', '页面', 0, 0, 'subscription/token', '', 1, 1, 50), - ('支付确认', @sub_parent_id, 'ios-cash', '/subscription/payment', '页面', 0, 0, 'subscription/payment', '', 1, 1, 60), - ('月用量', @sub_parent_id, 'ios-analytics', '/subscription/usage', '页面', 0, 0, 'subscription/usage', '', 1, 1, 70), - ('审计日志', @sub_parent_id, 'ios-paper', '/subscription/audit', '页面', 0, 0, 'subscription/audit', '', 1, 1, 80); + ('运营看板', @sub_parent_id, 'ios-speedometer', 'subscription_dashboard', '页面', 0, 0, 'subscription/dashboard', '', 1, 1, 10), + ('业务用户', @sub_parent_id, 'ios-people', 'subscription_user', '页面', 0, 0, 'subscription/user', '', 1, 1, 20), + ('套餐管理', @sub_parent_id, 'ios-pricetags', 'subscription_plan', '页面', 0, 0, 'subscription/plan', '', 1, 1, 30), + ('订阅列表', @sub_parent_id, 'ios-list', 'subscription_subscription', '页面', 0, 0, 'subscription/subscription', '', 1, 1, 40), + ('API Token', @sub_parent_id, 'ios-key', 'subscription_token', '页面', 0, 0, 'subscription/token', '', 1, 1, 50), + ('支付确认', @sub_parent_id, 'ios-cash', 'subscription_payment', '页面', 0, 0, 'subscription/payment', '', 1, 1, 60), + ('月用量', @sub_parent_id, 'ios-analytics', 'subscription_usage', '页面', 0, 0, 'subscription/usage', '', 1, 1, 70), + ('审计日志', @sub_parent_id, 'ios-paper', 'subscription_audit', '页面', 0, 0, 'subscription/audit', '', 1, 1, 80); -- ----------------------------------------------------------------------------- -- 方案二(可选):全部挂在根节点 parent_id=0,无父级目录(与 component-map 仍一致) @@ -48,12 +49,12 @@ VALUES INSERT INTO sys_menu (name, parent_id, icon, path, type, model_id, form_id, component, api_path, is_show_menu, is_show, sort) VALUES - ('运营看板', 0, 'ios-speedometer', '/subscription/dashboard', '页面', 0, 0, 'subscription/dashboard', '', 1, 1, 910), - ('业务用户', 0, 'ios-people', '/subscription/user', '页面', 0, 0, 'subscription/user', '', 1, 1, 920), - ('套餐管理', 0, 'ios-pricetags', '/subscription/plan', '页面', 0, 0, 'subscription/plan', '', 1, 1, 930), - ('订阅列表', 0, 'ios-list', '/subscription/subscription', '页面', 0, 0, 'subscription/subscription', '', 1, 1, 940), - ('API Token', 0, 'ios-key', '/subscription/token', '页面', 0, 0, 'subscription/token', '', 1, 1, 950), - ('支付确认', 0, 'ios-cash', '/subscription/payment', '页面', 0, 0, 'subscription/payment', '', 1, 1, 960), - ('月用量', 0, 'ios-analytics', '/subscription/usage', '页面', 0, 0, 'subscription/usage', '', 1, 1, 970), - ('审计日志', 0, 'ios-paper', '/subscription/audit', '页面', 0, 0, 'subscription/audit', '', 1, 1, 980); + ('运营看板', 0, 'ios-speedometer', 'subscription_dashboard', '页面', 0, 0, 'subscription/dashboard', '', 1, 1, 910), + ('业务用户', 0, 'ios-people', 'subscription_user', '页面', 0, 0, 'subscription/user', '', 1, 1, 920), + ('套餐管理', 0, 'ios-pricetags', 'subscription_plan', '页面', 0, 0, 'subscription/plan', '', 1, 1, 930), + ('订阅列表', 0, 'ios-list', 'subscription_subscription', '页面', 0, 0, 'subscription/subscription', '', 1, 1, 940), + ('API Token', 0, 'ios-key', 'subscription_token', '页面', 0, 0, 'subscription/token', '', 1, 1, 950), + ('支付确认', 0, 'ios-cash', 'subscription_payment', '页面', 0, 0, 'subscription/payment', '', 1, 1, 960), + ('月用量', 0, 'ios-analytics', 'subscription_usage', '页面', 0, 0, 'subscription/usage', '', 1, 1, 970), + ('审计日志', 0, 'ios-paper', 'subscription_audit', '页面', 0, 0, 'subscription/audit', '', 1, 1, 980); */ diff --git a/_docs/sql/003_home_menu_seed.sql b/_docs/sql/003_home_menu_seed.sql new file mode 100644 index 0000000..cfe923d --- /dev/null +++ b/_docs/sql/003_home_menu_seed.sql @@ -0,0 +1,29 @@ +-- ============================================================================= +-- sys_menu 首页(字段与 api/model/sys_menu.js、现有库中菜单数据格式一致) +-- +-- 参考库中同类数据示例: +-- path:首页为 home(无前导 /);订阅子页为 /subscription/xxx +-- component:与 admin/src/router/component-map.js 的 key 一致,可为 home/index 或 home/index.vue +-- icon:Material 图标名如 md-home(与系统页 system/sys_user.vue 等同风格) +-- ============================================================================= + +INSERT INTO sys_menu + (name, parent_id, icon, path, type, model_id, form_id, component, api_path, is_show_menu, is_show, sort) +VALUES + ( + '首页', + 0, + 'md-home', + 'home', + '页面', + 0, + 0, + 'home/index.vue', + '', + 1, + 1, + 0 + ); + +-- 若已存在 name=首页 或 path=home 的记录,请先删除或改值后再执行,避免重复。 +-- 若你希望 component 不带后缀,可将上面 'home/index.vue' 改为 'home/index'(component-map 两种 key 均已注册)。 diff --git a/_docs/sql/004_migrate_biz_users_to_biz_user.sql b/_docs/sql/004_migrate_biz_users_to_biz_user.sql new file mode 100644 index 0000000..4d0e5b0 --- /dev/null +++ b/_docs/sql/004_migrate_biz_users_to_biz_user.sql @@ -0,0 +1,36 @@ +-- ============================================================================= +-- 若早期已按旧版 001 建表 biz_users,而运行时报错查 biz_user,可执行本脚本迁移表名。 +-- 执行前备份数据库。若子表尚未创建,可跳过本脚本,直接 DROP biz_users 后重跑新版 001_biz_schema.sql。 +-- ============================================================================= + +-- 若不存在 biz_users 则无需执行 +-- 步骤:去外键引用 -> 重命名父表 ->(子表 FK 仍指向旧名时需重建,MySQL 8 重命名父表后约束名可能需检查) + +-- 1) 删除引用 biz_users 的外键(子表若已存在) +SET @db = DATABASE(); + +SET @sql = ( + SELECT GROUP_CONCAT(CONCAT('ALTER TABLE `', TABLE_NAME, '` DROP FOREIGN KEY `', CONSTRAINT_NAME, '`') SEPARATOR '; ') + FROM information_schema.KEY_COLUMN_USAGE + WHERE TABLE_SCHEMA = @db + AND REFERENCED_TABLE_NAME = 'biz_users' +); +-- 若上面为空则无子表 FK,可手动执行下面 RENAME + +-- 手工示例(按实际约束名调整): +-- ALTER TABLE `biz_subscriptions` DROP FOREIGN KEY `fk_biz_sub_user`; +-- ALTER TABLE `biz_api_tokens` DROP FOREIGN KEY `fk_biz_token_user`; +-- ALTER TABLE `biz_usage_monthly` DROP FOREIGN KEY `fk_biz_usage_user`; + +-- 2) 重命名业务用户表 +-- RENAME TABLE `biz_users` TO `biz_user`; + +-- 3) 重新添加外键(与 001_biz_schema.sql 一致) +-- ALTER TABLE `biz_subscriptions` +-- ADD CONSTRAINT `fk_biz_sub_user` FOREIGN KEY (`user_id`) REFERENCES `biz_user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE; +-- ALTER TABLE `biz_api_tokens` +-- ADD CONSTRAINT `fk_biz_token_user` FOREIGN KEY (`user_id`) REFERENCES `biz_user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE; +-- ALTER TABLE `biz_usage_monthly` +-- ADD CONSTRAINT `fk_biz_usage_user` FOREIGN KEY (`user_id`) REFERENCES `biz_user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE; + +-- 更简单做法(无重要数据):DROP 子表与 biz_users,再执行新版 001_biz_schema.sql 全量重建。 diff --git a/admin/config/README.md b/admin/config/README.md index 3b2afb7..ec82622 100644 --- a/admin/config/README.md +++ b/admin/config/README.md @@ -39,8 +39,8 @@ admin/ ```javascript { - apiUrl: 'http://localhost:9098/admin_api/', - uploadUrl: 'http://localhost:9098/admin_api/upload', + apiUrl: 'http://localhost:9099/admin_api/', + uploadUrl: 'http://localhost:9099/admin_api/upload', debug: true // 开发环境显示调试信息 } ``` @@ -114,7 +114,7 @@ HTTP 工具已自动使用 `apiUrl` 作为基础路径: ```javascript // 无需手动拼接 apiUrl,框架会自动处理 this.$http.get('/user/list') -// 实际请求: http://localhost:9098/admin_api/user/list +// 实际请求: http://localhost:9099/admin_api/user/list ``` --- @@ -141,8 +141,8 @@ const baseConfig = { // 开发环境配置 const developmentConfig = { ...baseConfig, - apiUrl: 'http://localhost:9098/admin_api/', // 修改开发环境 API 地址 - uploadUrl: 'http://localhost:9098/admin_api/upload', + apiUrl: 'http://localhost:9099/admin_api/', // 修改开发环境 API 地址 + uploadUrl: 'http://localhost:9099/admin_api/upload', debug: true } @@ -162,8 +162,8 @@ const productionConfig = { ```javascript const developmentConfig = { ...baseConfig, - apiUrl: 'http://localhost:9098/admin_api/', - uploadUrl: 'http://localhost:9098/admin_api/upload', + apiUrl: 'http://localhost:9099/admin_api/', + uploadUrl: 'http://localhost:9099/admin_api/upload', // 自定义配置 enableMock: true, diff --git a/admin/config/index.js b/admin/config/index.js index ebf071a..f039c7f 100644 --- a/admin/config/index.js +++ b/admin/config/index.js @@ -17,8 +17,8 @@ const baseConfig = { // 本地开发(默认) const developmentConfig = { ...baseConfig, - apiUrl: 'http://localhost:9098/admin_api/', - uploadUrl: 'http://localhost:9098/admin_api/upload', + apiUrl: 'http://localhost:9099/admin_api/', + uploadUrl: 'http://localhost:9099/admin_api/upload', debug: true } diff --git a/admin/src/router/component-map.js b/admin/src/router/component-map.js index aa9701b..dff8e26 100644 --- a/admin/src/router/component-map.js +++ b/admin/src/router/component-map.js @@ -1,4 +1,5 @@ // 组件映射表:后端菜单返回的 component 路径需与此处 key 一致(不含 .vue) +import HomeIndex from '../views/home/index.vue' import TestPage from '../views/test/test.vue' import SubscriptionDashboard from '../views/subscription/dashboard.vue' import SubscriptionUsers from '../views/subscription/users.vue' @@ -10,6 +11,9 @@ import SubscriptionUsage from '../views/subscription/usage.vue' import SubscriptionAuditLog from '../views/subscription/audit_log.vue' const componentMap = { + // 与 sys_menu.component 一致:库中常见为 home/index 或 home/index.vue + 'home/index': HomeIndex, + 'home/index.vue': HomeIndex, 'test/test': TestPage, 'subscription/dashboard': SubscriptionDashboard, 'subscription/user': SubscriptionUsers, diff --git a/admin/src/views/home/index.vue b/admin/src/views/home/index.vue new file mode 100644 index 0000000..55dc790 --- /dev/null +++ b/admin/src/views/home/index.vue @@ -0,0 +1,53 @@ + + + + + diff --git a/admin/src/views/subscription/audit_log.vue b/admin/src/views/subscription/audit_log.vue index 07f565c..4b680fa 100644 --- a/admin/src/views/subscription/audit_log.vue +++ b/admin/src/views/subscription/audit_log.vue @@ -1,32 +1,38 @@ @@ -105,26 +111,43 @@ export default { this.$Message.error((res && res.message) || '导出失败') } }, + resetQuery() { + this.param.seachOption = { key: 'action', value: '' } + this.load(1) + }, }, }