diff --git a/_docs/sql/001_biz_schema.sql b/_docs/sql/001_biz_schema.sql index a3c8f25..eb90693 100644 --- a/_docs/sql/001_biz_schema.sql +++ b/_docs/sql/001_biz_schema.sql @@ -12,8 +12,8 @@ CREATE TABLE IF NOT EXISTS `biz_user` ( `email` VARCHAR(120) NULL DEFAULT NULL, `company_name` VARCHAR(200) NULL DEFAULT NULL, `status` ENUM('active', 'disabled') NOT NULL DEFAULT 'active', - `created_at` DATETIME NOT NULL, - `updated_at` DATETIME NOT NULL, + `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_biz_user_mobile` (`mobile`), KEY `idx_biz_user_status` (`status`) @@ -34,8 +34,8 @@ CREATE TABLE IF NOT EXISTS `biz_plans` ( `sns_quota` INT NOT NULL DEFAULT 0, `enabled_features` JSON NULL, `status` ENUM('active', 'inactive') NOT NULL DEFAULT 'active', - `created_at` DATETIME NOT NULL, - `updated_at` DATETIME NOT NULL, + `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_biz_plans_code` (`plan_code`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='套餐'; @@ -71,8 +71,8 @@ CREATE TABLE IF NOT EXISTS `biz_api_tokens` ( `status` ENUM('active', 'revoked', 'expired') NOT NULL DEFAULT 'active', `expire_at` DATETIME NOT NULL, `last_used_at` DATETIME NULL DEFAULT NULL, - `created_at` DATETIME NOT NULL, - `updated_at` DATETIME NOT NULL, + `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_biz_token_hash` (`token_hash`), KEY `idx_biz_token_user` (`user_id`), @@ -92,8 +92,8 @@ CREATE TABLE IF NOT EXISTS `biz_usage_monthly` ( `friend_count` INT NOT NULL DEFAULT 0, `sns_count` INT NOT NULL DEFAULT 0, `active_user_count` INT NOT NULL DEFAULT 0, - `created_at` DATETIME NOT NULL, - `updated_at` DATETIME NOT NULL, + `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_biz_usage_user_month` (`user_id`, `stat_month`), KEY `idx_biz_usage_plan` (`plan_id`), diff --git a/_docs/sql/003_biz_audit.sql b/_docs/sql/003_biz_audit.sql index 249710d..bb64260 100644 --- a/_docs/sql/003_biz_audit.sql +++ b/_docs/sql/003_biz_audit.sql @@ -9,7 +9,7 @@ CREATE TABLE IF NOT EXISTS `biz_audit_log` ( `resource_type` VARCHAR(64) NOT NULL DEFAULT '', `resource_id` BIGINT UNSIGNED NULL DEFAULT NULL, `detail` JSON NULL, - `created_at` DATETIME NOT NULL, + `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_biz_audit_action` (`action`), KEY `idx_biz_audit_created` (`created_at`) diff --git a/_docs/sql/005_biz_timestamp_defaults_alter.sql b/_docs/sql/005_biz_timestamp_defaults_alter.sql new file mode 100644 index 0000000..000d113 --- /dev/null +++ b/_docs/sql/005_biz_timestamp_defaults_alter.sql @@ -0,0 +1,27 @@ +-- 已有库若 `created_at` / `updated_at` 无默认值,插入会失败(模型已关闭 Sequelize timestamps 且未声明时间字段时依赖库默认值)。 +-- 按需对已有表执行(新库直接执行 001/003 即可,无需本文件)。 + +SET NAMES utf8mb4; + +ALTER TABLE `biz_user` + MODIFY `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + MODIFY `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP; + +ALTER TABLE `biz_plans` + MODIFY `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + MODIFY `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP; + +ALTER TABLE `biz_subscriptions` + MODIFY `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + MODIFY `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP; + +ALTER TABLE `biz_api_tokens` + MODIFY `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + MODIFY `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP; + +ALTER TABLE `biz_usage_monthly` + MODIFY `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + MODIFY `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP; + +ALTER TABLE `biz_audit_log` + MODIFY `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP; diff --git a/api/model/biz_api_token.js b/api/model/biz_api_token.js index 8204dfb..da42fc0 100644 --- a/api/model/biz_api_token.js +++ b/api/model/biz_api_token.js @@ -38,7 +38,7 @@ module.exports = (db) => { }, { tableName: "biz_api_tokens", - timestamps: true, + timestamps: false, underscored: true, comment: "API Token", } diff --git a/api/model/biz_audit_log.js b/api/model/biz_audit_log.js index 730a0c9..29a67b6 100644 --- a/api/model/biz_audit_log.js +++ b/api/model/biz_audit_log.js @@ -34,10 +34,6 @@ module.exports = (db) => { type: Sequelize.JSON, allowNull: true, }, - created_at: { - type: Sequelize.DATE, - allowNull: false, - }, }, { tableName: "biz_audit_log", diff --git a/api/model/biz_plan.js b/api/model/biz_plan.js index 0d1ab1c..154fc07 100644 --- a/api/model/biz_plan.js +++ b/api/model/biz_plan.js @@ -48,7 +48,7 @@ module.exports = (db) => { }, { tableName: "biz_plans", - timestamps: true, + timestamps: false, underscored: true, comment: "套餐", } diff --git a/api/model/biz_subscription.js b/api/model/biz_subscription.js index 7b0f9b7..aa0920e 100644 --- a/api/model/biz_subscription.js +++ b/api/model/biz_subscription.js @@ -40,7 +40,7 @@ module.exports = (db) => { }, { tableName: "biz_subscriptions", - timestamps: true, + timestamps: false, underscored: true, comment: "订阅", } diff --git a/api/model/biz_usage_monthly.js b/api/model/biz_usage_monthly.js index 95192b9..ba96d10 100644 --- a/api/model/biz_usage_monthly.js +++ b/api/model/biz_usage_monthly.js @@ -30,7 +30,7 @@ module.exports = (db) => { }, { tableName: "biz_usage_monthly", - timestamps: true, + timestamps: false, underscored: true, comment: "月用量", } diff --git a/api/model/biz_user.js b/api/model/biz_user.js index 71ec842..d85bec6 100644 --- a/api/model/biz_user.js +++ b/api/model/biz_user.js @@ -39,7 +39,7 @@ module.exports = (db) => { // 与库表名一致:单数 biz_user(与模型名一致,避免部分环境下 tableName 未生效时落到默认表名 biz_user) tableName: "biz_user", freezeTableName: true, - timestamps: true, + timestamps: false, underscored: true, comment: "业务用户", } diff --git a/api/service/biz_admin_crud.js b/api/service/biz_admin_crud.js index 1d4af9e..88dc6d2 100644 --- a/api/service/biz_admin_crud.js +++ b/api/service/biz_admin_crud.js @@ -94,6 +94,19 @@ function buildSearchWhere(model, seachOption) { return { [key]: { [op.like]: `%${str}%` } }; } +/** 模型未声明但表中存在的列(列表/导出需要带出) */ +function extraListAttributes(modelName, model) { + if (modelName === "biz_audit_log") { + const tn = model.tableName; + return { + attributes: { + include: [[model.sequelize.col(`${tn}.created_at`), "created_at"]], + }, + }; + } + return {}; +} + async function page(modelName, body) { const model = getModel(modelName); const param = body.param || body; @@ -111,6 +124,7 @@ async function page(modelName, body) { offset, limit: pageSize, order: [["id", "DESC"]], + ...extraListAttributes(modelName, model), }); return { rows, count }; @@ -151,7 +165,7 @@ async function detail(modelName, query) { if (id === undefined || id === null || id === "") { throw new Error("缺少 id"); } - const row = await model.findByPk(id); + const row = await model.findByPk(id, extraListAttributes(modelName, model)); return row; } @@ -160,6 +174,7 @@ async function all(modelName) { const rows = await model.findAll({ limit: 2000, order: [["id", "DESC"]], + ...extraListAttributes(modelName, model), }); return rows; } @@ -172,6 +187,7 @@ async function exportCsv(modelName, body) { where, limit: 10000, order: [["id", "DESC"]], + ...extraListAttributes(modelName, model), }); return { rows }; } diff --git a/api/service/biz_audit_service.js b/api/service/biz_audit_service.js index 91afbb3..254e1ef 100644 --- a/api/service/biz_audit_service.js +++ b/api/service/biz_audit_service.js @@ -20,7 +20,6 @@ async function logAudit(p) { resource_type: p.resource_type || "", resource_id: p.resource_id != null ? p.resource_id : null, detail: p.detail || null, - created_at: new Date(), }); } catch (e) { logs.error("[biz_audit] 写入失败", e);