// +---------------------------------------------------------------------- declare(strict_types=1); namespace app\appmall\model; use app\appmall\library\AppmallSchema; use ywxapp\model\BaseModel; /** * 插件授权(中心站) * 表:appmall_addon_licenses * @mixin \think\Model */ class AddonLicense extends BaseModel { protected function getOptions(): array { return [ 'strict' => true, 'name' => 'appmall_addon_licenses', 'autoWriteTimestamp' => 'int', 'readonly' => ['uid'], 'createTime' => 'create_at', 'updateTime' => 'update_at', ]; } /** * 表结构自愈:缺表建表 + 老库扩展列补齐(事实源 app/appmall/install.sql)。 */ public static function ensureSchema(): void { AppmallSchema::ensureTable('appmall_addon_licenses', [ 'status' => "tinyint NOT NULL DEFAULT '1' COMMENT '1=有效 0=已吊销'", 'site_id' => "int unsigned NOT NULL DEFAULT '0' COMMENT '绑定站点'", 'domain' => "varchar(255) DEFAULT NULL COMMENT '绑定域名(站点授权)'", 'expire_time' => "int unsigned DEFAULT 0 COMMENT '0为永久'", // 授权跨版本:addon_name 是「同一插件所有版本共享一份授权」的判定主键 'addon_name' => "varchar(50) NOT NULL DEFAULT '' COMMENT '插件标识(跨版本授权判定主键)'", 'max_major' => "smallint unsigned NOT NULL DEFAULT 0 COMMENT '已购覆盖的大版本上限,0=不限(买断式)'", 'seats' => "int unsigned NOT NULL DEFAULT 1 COMMENT '可绑定站点数(多站点套餐)'", 'trial_end' => "int unsigned NOT NULL DEFAULT 0 COMMENT '试用到期时间,0=非试用或已转正'", 'period' => "int unsigned NOT NULL DEFAULT 0 COMMENT '订阅周期天数快照(0=买断)'", ]); } /** * 模型一被使用(含静态查询)就先兜底建表,任何入口都不会再撞 1146。 */ protected function initialize() { parent::initialize(); self::ensureSchema(); } /** * 关联插件信息(核心插件注册表) * @return \think\model\relation\BelongsTo */ public function addon() { return $this->belongsTo(\ywxapp\model\CommonAddon::class, 'aid', 'id'); } /** * 生成安全签名(防链接泄漏) * @param string $licenseKey 授权码 * @param int $aid 插件ID * @param int $timestamp 时间戳 * @param string $secret 签名密钥(仅作 HMAC key,不要拼进消息) * @return string */ public function generateSign(string $licenseKey, int $aid, int $timestamp, string $secret): string { $str = $licenseKey . $aid . $timestamp; return hash_hmac('sha256', $str, $secret); } }