mirror of
https://gitee.com/ulthon/ulthon_admin.git
synced 2026-10-29 08:58:22 +08:00
refactor(install): 种子退役,migrate:run 一条命令完成建表与初始数据
新增 InitDataAlignService 六域对齐(超管/角色/权限节点/配置/快捷入口/商城演示)与 20260909130000 迁移;SystemAdmin 快照以字面量哈希入库;删除种子三件与安装锁依赖,seeds 目录留 .gitkeep;README/tests/规则等 11 处说明同步。 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -1,141 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace base\admin\service;
|
||||
|
||||
use app\admin\model\MallCate;
|
||||
use app\admin\model\MallGoods;
|
||||
use app\admin\model\MallTag;
|
||||
use app\admin\model\SystemAdmin;
|
||||
use app\admin\model\SystemAuth;
|
||||
use app\admin\model\SystemAuthNode;
|
||||
use app\admin\model\SystemConfig;
|
||||
use app\admin\model\SystemMenu;
|
||||
use app\admin\model\SystemQuick;
|
||||
use app\common\constants\AdminConstant;
|
||||
|
||||
class AdminInitServiceBase
|
||||
{
|
||||
protected $output = null;
|
||||
|
||||
public function __construct($output)
|
||||
{
|
||||
$this->output = $output;
|
||||
}
|
||||
|
||||
public function init()
|
||||
{
|
||||
$this->initAdmin();
|
||||
$this->initAuth();
|
||||
$this->initConfig();
|
||||
$this->initMenu();
|
||||
$this->initQuick();
|
||||
$this->initMall();
|
||||
}
|
||||
|
||||
public function initMall()
|
||||
{
|
||||
$output = $this->output;
|
||||
|
||||
$output->writeln('开始初始化商城案例');
|
||||
|
||||
$list_cate = $this->requireData('MallCate');
|
||||
|
||||
$list_goods = $this->requireData('MallGoods');
|
||||
|
||||
foreach ($list_cate as $data_cate) {
|
||||
$model_cate = MallCate::create($data_cate);
|
||||
|
||||
foreach ($list_goods as $data_goods) {
|
||||
$data_goods['cate_id'] = $model_cate->id;
|
||||
MallGoods::create($data_goods);
|
||||
}
|
||||
}
|
||||
|
||||
$list_tag = $this->requireData('MallTag');
|
||||
|
||||
$this->installData(MallTag::class, $list_tag);
|
||||
}
|
||||
|
||||
public function initQuick()
|
||||
{
|
||||
$output = $this->output;
|
||||
|
||||
$output->writeln('开始初始化快捷入口');
|
||||
|
||||
$list_quick = $this->requireData('SystemQuick');
|
||||
|
||||
$this->installData(SystemQuick::class, $list_quick);
|
||||
}
|
||||
|
||||
public function initMenu()
|
||||
{
|
||||
$output = $this->output;
|
||||
|
||||
$output->writeln('开始初始化系统菜单');
|
||||
|
||||
$list_menu = $this->requireData('SystemMenu');
|
||||
|
||||
$this->installData(SystemMenu::class, $list_menu);
|
||||
}
|
||||
|
||||
public function initConfig()
|
||||
{
|
||||
$output = $this->output;
|
||||
$output->writeln('开始初始化系统设置');
|
||||
|
||||
$list_config = $this->requireData('SystemConfig');
|
||||
|
||||
$this->installData(SystemConfig::class, $list_config);
|
||||
}
|
||||
|
||||
public function initAdmin()
|
||||
{
|
||||
$output = $this->output;
|
||||
|
||||
$output->writeln('创建超级管理员');
|
||||
|
||||
$model_admin = SystemAdmin::find(AdminConstant::SUPER_ADMIN_ID);
|
||||
|
||||
if (empty($model_admin)) {
|
||||
$model_admin = new SystemAdmin();
|
||||
$model_admin->id = AdminConstant::SUPER_ADMIN_ID;
|
||||
$model_admin->head_img = '/static/admin/images/head.jpg';
|
||||
$model_admin->username = 'admin';
|
||||
$model_admin->password = password(123456);
|
||||
$model_admin->status = 1;
|
||||
$model_admin->save();
|
||||
$output->writeln('创建超级管理员成功');
|
||||
} else {
|
||||
$output->writeln('超级管理员已存在,无需初始化');
|
||||
}
|
||||
}
|
||||
|
||||
public function initAuth()
|
||||
{
|
||||
$output = $this->output;
|
||||
|
||||
$output->writeln('开始初始化权限');
|
||||
|
||||
$list_auth = $this->requireData('SystemAuth');
|
||||
|
||||
$this->installData(SystemAuth::class, $list_auth);
|
||||
|
||||
$output->writeln('开始初始化权限节点');
|
||||
|
||||
$list_auth_node = $this->requireData('SystemAuthNode');
|
||||
|
||||
$this->installData(SystemAuthNode::class, $list_auth_node);
|
||||
}
|
||||
|
||||
protected function installData($model_name, $list)
|
||||
{
|
||||
foreach ($list as $key => $value) {
|
||||
$model_name::create($value);
|
||||
}
|
||||
}
|
||||
|
||||
protected function requireData($table_name)
|
||||
{
|
||||
return include __DIR__ . '/adminInitData/' . $table_name . '.php';
|
||||
}
|
||||
}
|
||||
262
extend/base/admin/service/InitDataAlignServiceBase.php
Normal file
262
extend/base/admin/service/InitDataAlignServiceBase.php
Normal file
@@ -0,0 +1,262 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @internal-framework
|
||||
*
|
||||
* 此文件为框架核心服务
|
||||
*
|
||||
* 用途:初始化数据对齐服务(六大数据域按快照对齐,migrate:run 唯一安装路径)
|
||||
* 维护者:框架维护者
|
||||
*
|
||||
* 背景:种子机制退役后,adminInitData/*.php 快照是初始数据的唯一数据源,
|
||||
* 由本服务随数据型 migration 安装(migrate:run 是文档化安装命令,phinxlog
|
||||
* 保证幂等)。取代原 AdminInitServiceBase 的种子初始化。
|
||||
*
|
||||
* 对齐语义(保守,尊重用户自定义):
|
||||
* - 超管 :id=1 不存在才插入;密码永不覆盖(软删行存在同样视为已存在,不复活)
|
||||
* - 角色 :按显式 id 逐行只插(含游客权限 id=6)
|
||||
* - 权限节点:快照为空数组,直接跳过(该表由 auth 注解动态扫描生成)
|
||||
* - 配置 :按 (group, name) check-then-insert(表无唯一键,纯应用层判重),
|
||||
* 只插缺失不覆盖 value;发生过插入则清一次 sysconfig 缓存
|
||||
* - 快捷入口:按 href 只插缺失;软删行存在视为已存在,不复活
|
||||
* - 商城演示:分表守卫(cate 空则插分类;goods 空则插商品并注入分类 id;
|
||||
* tag 空则插标签),三守卫相互独立
|
||||
*
|
||||
* 硬性实现约束:auth/quick/admin 三表均有 delete_time,存在性检查必须用
|
||||
* Db::name 原生查询绕过软删除作用域——否则软删行被判「缺失」→ INSERT →
|
||||
* 主键冲突崩溃或复活用户删除的行。沿用 MenuAlignServiceBase 已验证模式。
|
||||
*/
|
||||
|
||||
namespace base\admin\service;
|
||||
|
||||
use think\console\Output;
|
||||
use think\facade\Db;
|
||||
|
||||
class InitDataAlignServiceBase
|
||||
{
|
||||
/**
|
||||
* @var Output|null 命令行输出(migration 场景传入,便于追踪对齐过程)
|
||||
*/
|
||||
protected $output = null;
|
||||
|
||||
public function __construct($output = null)
|
||||
{
|
||||
$this->output = $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* 按快照对齐全部初始数据域(幂等,可重复执行).
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function align(): void
|
||||
{
|
||||
$this->writeln('开始对齐初始化数据(超管/角色/权限节点/配置/快捷入口/商城演示)');
|
||||
|
||||
$this->alignAdmin();
|
||||
$this->alignAuth();
|
||||
$this->alignAuthNode();
|
||||
$this->alignConfig();
|
||||
$this->alignQuick();
|
||||
$this->alignMall();
|
||||
|
||||
$this->writeln('初始化数据对齐完成');
|
||||
}
|
||||
|
||||
/**
|
||||
* 超管对齐:id=1 不存在才插入,密码永不覆盖.
|
||||
*/
|
||||
protected function alignAdmin(): void
|
||||
{
|
||||
$snapshot = include __DIR__ . '/adminInitData/SystemAdmin.php';
|
||||
$now = time();
|
||||
|
||||
foreach ($snapshot as $row) {
|
||||
// 原生查询不过滤软删除:软删的超管行仍占用主键,视为已存在,不复活不覆盖
|
||||
$exists = Db::name('system_admin')->where('id', $row['id'])->find();
|
||||
|
||||
if (!empty($exists)) {
|
||||
$this->writeln(' - 超管 id=' . $row['id'] . ' 已存在,跳过(密码永不覆盖)');
|
||||
continue;
|
||||
}
|
||||
|
||||
$data = $row;
|
||||
$data['create_time'] = $now;
|
||||
$data['update_time'] = $now;
|
||||
$data['delete_time'] = 0;
|
||||
Db::name('system_admin')->insert($data);
|
||||
$this->writeln(' - 新增超管 id=' . $row['id'] . ' ' . $row['username']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 角色对齐:按显式 id 逐行只插(快照含管理员 id=1 与游客权限 id=6).
|
||||
*/
|
||||
protected function alignAuth(): void
|
||||
{
|
||||
$snapshot = include __DIR__ . '/adminInitData/SystemAuth.php';
|
||||
$now = time();
|
||||
|
||||
foreach ($snapshot as $row) {
|
||||
// 原生查询不过滤软删除:软删的角色行仍占用主键,视为已存在,不复活
|
||||
$exists = Db::name('system_auth')->where('id', $row['id'])->find();
|
||||
|
||||
if (!empty($exists)) {
|
||||
$this->writeln(' - 角色 id=' . $row['id'] . ' ' . $row['title'] . ' 已存在,跳过');
|
||||
continue;
|
||||
}
|
||||
|
||||
$data = $row;
|
||||
$data['create_time'] = $now;
|
||||
$data['update_time'] = $now;
|
||||
$data['delete_time'] = 0;
|
||||
Db::name('system_auth')->insert($data);
|
||||
$this->writeln(' - 新增角色 id=' . $row['id'] . ' ' . $row['title']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 权限节点对齐:快照为空数组,直接跳过(该表由 auth 注解动态扫描生成).
|
||||
*/
|
||||
protected function alignAuthNode(): void
|
||||
{
|
||||
$this->writeln(' - 权限节点由注解动态扫描生成,跳过快照对齐');
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置对齐:按 (group, name) check-then-insert,只插缺失不覆盖 value;
|
||||
* 表无唯一键,判重纯靠应用层;发生过插入则清一次 sysconfig 缓存.
|
||||
*/
|
||||
protected function alignConfig(): void
|
||||
{
|
||||
$snapshot = include __DIR__ . '/adminInitData/SystemConfig.php';
|
||||
$now = time();
|
||||
$inserted = 0;
|
||||
|
||||
foreach ($snapshot as $row) {
|
||||
$exists = Db::name('system_config')
|
||||
->where('group', $row['group'])
|
||||
->where('name', $row['name'])
|
||||
->find();
|
||||
|
||||
if (!empty($exists)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$data = $row;
|
||||
$data['create_time'] = $now;
|
||||
$data['update_time'] = $now;
|
||||
Db::name('system_config')->insert($data);
|
||||
++$inserted;
|
||||
}
|
||||
|
||||
$this->writeln(' - 系统配置新增 ' . $inserted . ' 条(缺失补齐,不覆盖已有 value)');
|
||||
|
||||
if ($inserted > 0) {
|
||||
TriggerServiceBase::updateSysconfig();
|
||||
$this->writeln(' - 已刷新系统设置缓存');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 快捷入口对齐:按 href 只插缺失;软删行存在视为已存在,不复活.
|
||||
*/
|
||||
protected function alignQuick(): void
|
||||
{
|
||||
$snapshot = include __DIR__ . '/adminInitData/SystemQuick.php';
|
||||
$now = time();
|
||||
$inserted = 0;
|
||||
|
||||
foreach ($snapshot as $row) {
|
||||
// 原生查询不过滤软删除:软删的快捷入口仍占用 href 语义,视为已存在,不复活
|
||||
$exists = Db::name('system_quick')->where('href', $row['href'])->find();
|
||||
|
||||
if (!empty($exists)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$data = $row;
|
||||
$data['create_time'] = $now;
|
||||
$data['update_time'] = $now;
|
||||
$data['delete_time'] = 0;
|
||||
Db::name('system_quick')->insert($data);
|
||||
++$inserted;
|
||||
}
|
||||
|
||||
$this->writeln(' - 快捷入口新增 ' . $inserted . ' 条(按 href 判重)');
|
||||
}
|
||||
|
||||
/**
|
||||
* 商城演示对齐:分表守卫(三守卫相互独立):
|
||||
* - cate 空则插分类(快照无显式 id,捕获自增 id)
|
||||
* - goods 空则插商品,注入分类 id(复刻旧 initMall 逻辑)
|
||||
* - tag 空则插标签(快照含显式 id 1-4)
|
||||
*/
|
||||
protected function alignMall(): void
|
||||
{
|
||||
$now = time();
|
||||
|
||||
// 分类表守卫:空则插入快照分类,捕获新自增 id 供商品注入
|
||||
$cateId = 0;
|
||||
if ($this->isTableEmpty('mall_cate')) {
|
||||
$snapshotCate = include __DIR__ . '/adminInitData/MallCate.php';
|
||||
foreach ($snapshotCate as $row) {
|
||||
$data = $row;
|
||||
$data['create_time'] = $now;
|
||||
$data['update_time'] = $now;
|
||||
$data['delete_time'] = 0;
|
||||
Db::name('mall_cate')->insert($data);
|
||||
$cateId = (int) Db::name('mall_cate')->getLastInsID();
|
||||
$this->writeln(' - 新增商品分类 id=' . $cateId . ' ' . $row['title']);
|
||||
}
|
||||
} else {
|
||||
$this->writeln(' - 商品分类表已有数据,跳过');
|
||||
}
|
||||
|
||||
// 商品表守卫:空则插入快照商品,cate_id 注入分类 id(无快照分类时取库内首个分类)
|
||||
if ($this->isTableEmpty('mall_goods')) {
|
||||
if ($cateId === 0) {
|
||||
$cateId = (int) Db::name('mall_cate')->order('id', 'asc')->value('id');
|
||||
}
|
||||
|
||||
$snapshotGoods = include __DIR__ . '/adminInitData/MallGoods.php';
|
||||
foreach ($snapshotGoods as $row) {
|
||||
$data = $row;
|
||||
$data['cate_id'] = $cateId;
|
||||
$data['create_time'] = $now;
|
||||
$data['update_time'] = $now;
|
||||
$data['delete_time'] = 0;
|
||||
Db::name('mall_goods')->insert($data);
|
||||
$this->writeln(' - 新增商品 ' . $row['title'] . ' (cate_id=' . $cateId . ')');
|
||||
}
|
||||
} else {
|
||||
$this->writeln(' - 商品表已有数据,跳过');
|
||||
}
|
||||
|
||||
// 标签表守卫:空则插入快照标签(显式 id,快照行自带时间戳与 delete_time)
|
||||
if ($this->isTableEmpty('mall_tag')) {
|
||||
$snapshotTag = include __DIR__ . '/adminInitData/MallTag.php';
|
||||
foreach ($snapshotTag as $row) {
|
||||
Db::name('mall_tag')->insert($row);
|
||||
$this->writeln(' - 新增商品标签 id=' . $row['id'] . ' ' . $row['title']);
|
||||
}
|
||||
} else {
|
||||
$this->writeln(' - 商品标签表已有数据,跳过');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断表是否为空(原生查询,不受任何作用域影响).
|
||||
*/
|
||||
protected function isTableEmpty(string $table): bool
|
||||
{
|
||||
return Db::name($table)->count() == 0;
|
||||
}
|
||||
|
||||
protected function writeln(string $message): void
|
||||
{
|
||||
if ($this->output !== null) {
|
||||
$this->output->writeln($message);
|
||||
}
|
||||
}
|
||||
}
|
||||
24
extend/base/admin/service/adminInitData/SystemAdmin.php
Normal file
24
extend/base/admin/service/adminInitData/SystemAdmin.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
/**
|
||||
* @internal-framework
|
||||
*
|
||||
* 此文件为框架核心初始化数据
|
||||
*
|
||||
* 用途:超级管理员初始化(password 为 password('123456') 的 40 位字面量哈希,
|
||||
* 不得调用 password() 生成,保证迁移上下文可用且结果确定)
|
||||
* 维护者:框架维护者
|
||||
*
|
||||
* 注意:此文件属于框架内核,业务开发者不应修改
|
||||
*/
|
||||
|
||||
$ul_system_admin = array(
|
||||
array(
|
||||
"id" => 1,
|
||||
"head_img" => "/static/admin/images/head.jpg",
|
||||
"username" => "admin",
|
||||
"password" => "ac1e198122f74121a69b7bc9048561973a433daa",
|
||||
"status" => 1,
|
||||
)
|
||||
);
|
||||
|
||||
return $ul_system_admin;
|
||||
Reference in New Issue
Block a user