更新代码
This commit is contained in:
@@ -1,93 +1,25 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/beego/beego/v2/client/orm"
|
||||
)
|
||||
|
||||
var adminRoleTenantColOnce sync.Once
|
||||
var adminRoleCustomColOnce sync.Once
|
||||
|
||||
// EnsureAdminRoleTenantColumn 为角色表补齐 tenant_id 列(历史库可能早于租户隔离特性建表)。
|
||||
// 字段已存在时 MySQL 报 duplicate column,统一忽略,不阻断主流程。
|
||||
func EnsureAdminRoleTenantColumn() {
|
||||
if Orm == nil {
|
||||
return
|
||||
}
|
||||
adminRoleTenantColOnce.Do(func() {
|
||||
_, _ = Orm.Raw(`
|
||||
ALTER TABLE yz_system_admin_role
|
||||
ADD COLUMN tenant_id bigint unsigned NOT NULL DEFAULT 0 COMMENT '租户ID 0-平台/全局 >0-具体租户'
|
||||
`).Exec()
|
||||
})
|
||||
}
|
||||
|
||||
// EnsureAdminRoleCustomColumn 为角色表补齐 is_custom 列(历史库可能早于自定义角色特性建表)。
|
||||
// 字段已存在时 MySQL 报 duplicate column,统一忽略,不阻断主流程。
|
||||
func EnsureAdminRoleCustomColumn() {
|
||||
if Orm == nil {
|
||||
return
|
||||
}
|
||||
adminRoleCustomColOnce.Do(func() {
|
||||
_, _ = Orm.Raw(`
|
||||
ALTER TABLE yz_system_admin_role
|
||||
ADD COLUMN is_custom tinyint unsigned NOT NULL DEFAULT 0 COMMENT '自定义角色 0-系统/平台预置 1-租户自建'
|
||||
`).Exec()
|
||||
})
|
||||
}
|
||||
|
||||
// EnsureDefaultTenantRoles 保证指定租户(cid=2)的默认角色齐全:
|
||||
// - 租户管理员:全权限(rights 为空,见 permission.go 空权限即放行)
|
||||
// - 部门负责人
|
||||
// - 员工
|
||||
// GetTenantAdminRole 返回全局「租户管理员」角色(cid=2, tenant_id=0)的 ID。
|
||||
//
|
||||
// 默认角色按租户隔离:每个租户首次访问角色列表时补建自己的一套,互不影响。
|
||||
// 缺失的自动插入;已存在(同名同 cid 同租户)的不动。任何失败均静默忽略不阻断主流程。
|
||||
func EnsureDefaultTenantRoles(tid uint64) {
|
||||
if Orm == nil || tid == 0 {
|
||||
return
|
||||
}
|
||||
// 确保 tenant_id / is_custom 列存在(兼容老表)
|
||||
EnsureAdminRoleTenantColumn()
|
||||
EnsureAdminRoleCustomColumn()
|
||||
|
||||
defaults := []string{"租户管理员", "部门负责人", "员工"}
|
||||
for _, name := range defaults {
|
||||
cnt, err := Orm.QueryTable(new(AdminRole)).
|
||||
Filter("cid", 2).
|
||||
Filter("tenant_id", tid).
|
||||
Filter("name", name).
|
||||
Count()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if cnt > 0 {
|
||||
continue
|
||||
}
|
||||
role := &AdminRole{
|
||||
TenantID: tid,
|
||||
Cid: 2,
|
||||
Name: name,
|
||||
Status: 1,
|
||||
Rights: nil, // 空权限 = 全权限(permission.go)
|
||||
}
|
||||
_, _ = Orm.Insert(role)
|
||||
}
|
||||
}
|
||||
|
||||
// GetTenantAdminRole 返回指定租户(cid=2)的「租户管理员」角色 ID。
|
||||
// 角色不存在时先调用 EnsureDefaultTenantRoles 补建再查;任何失败返回 (0, err)。
|
||||
// 普通租户(租户第一个账号)与租户管理员实为同一概念,均绑定此角色以获得 backend 全权限。
|
||||
func GetTenantAdminRole(tid uint64) (uint64, error) {
|
||||
if Orm == nil || tid == 0 {
|
||||
// 角色模型:租户端的「租户管理员 / 租户用户」为全局共用角色(tenant_id=0),
|
||||
// 所有租户共享同一批记录,由平台端角色管理统一维护;
|
||||
// 角色数据走 SQL(docs/sql/update_role_system.sql),代码不做自动补建。
|
||||
//
|
||||
// 租户的第一个账号即租户管理员,绑定此角色以获得 backend 全权限。
|
||||
// 任何失败返回 (0, err)。
|
||||
func GetTenantAdminRole() (uint64, error) {
|
||||
if Orm == nil {
|
||||
return 0, orm.ErrNoRows
|
||||
}
|
||||
EnsureDefaultTenantRoles(tid)
|
||||
var role AdminRole
|
||||
if err := Orm.QueryTable(new(AdminRole)).
|
||||
Filter("cid", 2).
|
||||
Filter("tenant_id", tid).
|
||||
Filter("tenant_id", 0).
|
||||
Filter("name", "租户管理员").
|
||||
One(&role); err != nil {
|
||||
return 0, err
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/beego/beego/v2/client/orm"
|
||||
@@ -54,61 +53,6 @@ func (m *CmsArticle) TableName() string {
|
||||
return "yz_cms_article"
|
||||
}
|
||||
|
||||
var cmsArticleTablesOnce sync.Once
|
||||
|
||||
// EnsureCmsArticleTables 首次使用时自动建表(若不存在)。
|
||||
func EnsureCmsArticleTables() error {
|
||||
var err error
|
||||
cmsArticleTablesOnce.Do(func() {
|
||||
_, err = Orm.Raw(`
|
||||
CREATE TABLE IF NOT EXISTS yz_cms_article_category (
|
||||
id bigint unsigned NOT NULL AUTO_INCREMENT,
|
||||
tid bigint unsigned NOT NULL DEFAULT 0,
|
||||
cid bigint unsigned NOT NULL DEFAULT 0,
|
||||
name varchar(100) NOT NULL DEFAULT '',
|
||||
image varchar(500) NOT NULL DEFAULT '',
|
||||
` + "`desc`" + ` varchar(500) NOT NULL DEFAULT '',
|
||||
sort int NOT NULL DEFAULT 0,
|
||||
status tinyint NOT NULL DEFAULT 1,
|
||||
create_time datetime NOT NULL,
|
||||
update_time datetime DEFAULT NULL,
|
||||
delete_time datetime DEFAULT NULL,
|
||||
PRIMARY KEY (id),
|
||||
KEY idx_tid_cid (tid, cid)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4`).Exec()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err = Orm.Raw(`
|
||||
CREATE TABLE IF NOT EXISTS yz_cms_article (
|
||||
id bigint unsigned NOT NULL AUTO_INCREMENT,
|
||||
tid bigint unsigned NOT NULL DEFAULT 0,
|
||||
title varchar(255) NOT NULL DEFAULT '',
|
||||
author varchar(100) NOT NULL DEFAULT '',
|
||||
cate_id bigint unsigned NOT NULL DEFAULT 0,
|
||||
content mediumtext,
|
||||
` + "`desc`" + ` varchar(500) NOT NULL DEFAULT '',
|
||||
image varchar(500) NOT NULL DEFAULT '',
|
||||
is_trans tinyint NOT NULL DEFAULT 0,
|
||||
transurl varchar(500) DEFAULT NULL,
|
||||
status tinyint NOT NULL DEFAULT 0,
|
||||
top tinyint NOT NULL DEFAULT 0,
|
||||
recommend tinyint NOT NULL DEFAULT 0,
|
||||
views int NOT NULL DEFAULT 0,
|
||||
likes int NOT NULL DEFAULT 0,
|
||||
publisher_id bigint unsigned DEFAULT NULL,
|
||||
publish_time datetime DEFAULT NULL,
|
||||
create_time datetime NOT NULL,
|
||||
update_time datetime DEFAULT NULL,
|
||||
delete_time datetime DEFAULT NULL,
|
||||
PRIMARY KEY (id),
|
||||
KEY idx_tid_status (tid, status),
|
||||
KEY idx_cate_id (cate_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4`).Exec()
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func CmsCategoryNameMap(tid uint64, ids []uint64) map[uint64]string {
|
||||
out := make(map[uint64]string)
|
||||
if len(ids) == 0 {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -43,47 +42,4 @@ func (m *CmsProductCategory) TableName() string {
|
||||
return "yz_cms_product_category"
|
||||
}
|
||||
|
||||
var cmsProductTablesOnce sync.Once
|
||||
|
||||
// EnsureCmsProductTables 首次使用时自动建表(若不存在)。
|
||||
func EnsureCmsProductTables() error {
|
||||
var err error
|
||||
cmsProductTablesOnce.Do(func() {
|
||||
_, err = Orm.Raw(`
|
||||
CREATE TABLE IF NOT EXISTS yz_cms_product (
|
||||
id bigint unsigned NOT NULL AUTO_INCREMENT,
|
||||
tid bigint unsigned NOT NULL DEFAULT 0,
|
||||
title varchar(100) NOT NULL DEFAULT '',
|
||||
thumb varchar(500) NOT NULL DEFAULT '',
|
||||
` + "`desc`" + ` varchar(500) NOT NULL DEFAULT '',
|
||||
content mediumtext,
|
||||
url varchar(500) NOT NULL DEFAULT '',
|
||||
sort int NOT NULL DEFAULT 0,
|
||||
status tinyint NOT NULL DEFAULT 1,
|
||||
create_time datetime NOT NULL,
|
||||
update_time datetime DEFAULT NULL,
|
||||
delete_time datetime DEFAULT NULL,
|
||||
PRIMARY KEY (id),
|
||||
KEY idx_tid_status (tid, status)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4`).Exec()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err = Orm.Raw(`
|
||||
CREATE TABLE IF NOT EXISTS yz_cms_product_category (
|
||||
id bigint unsigned NOT NULL AUTO_INCREMENT,
|
||||
tid bigint unsigned NOT NULL DEFAULT 0,
|
||||
title varchar(100) NOT NULL DEFAULT '',
|
||||
pid bigint unsigned NOT NULL DEFAULT 0,
|
||||
` + "`desc`" + ` varchar(500) NOT NULL DEFAULT '',
|
||||
sort int NOT NULL DEFAULT 0,
|
||||
status tinyint NOT NULL DEFAULT 1,
|
||||
create_time datetime NOT NULL,
|
||||
update_time datetime DEFAULT NULL,
|
||||
delete_time datetime DEFAULT NULL,
|
||||
PRIMARY KEY (id),
|
||||
KEY idx_tid_pid (tid, pid)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4`).Exec()
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -43,49 +42,4 @@ func (m *CmsSolutionCategory) TableName() string {
|
||||
return "yz_cms_solution_category"
|
||||
}
|
||||
|
||||
var cmsSolutionTablesOnce sync.Once
|
||||
|
||||
// EnsureCmsSolutionTables 首次使用时自动建表(若不存在)。
|
||||
func EnsureCmsSolutionTables() error {
|
||||
var err error
|
||||
cmsSolutionTablesOnce.Do(func() {
|
||||
_, err = Orm.Raw(`
|
||||
CREATE TABLE IF NOT EXISTS yz_cms_solution (
|
||||
id bigint unsigned NOT NULL AUTO_INCREMENT,
|
||||
tid bigint unsigned NOT NULL DEFAULT 0,
|
||||
title varchar(100) NOT NULL DEFAULT '',
|
||||
thumb varchar(500) NOT NULL DEFAULT '',
|
||||
` + "`desc`" + ` varchar(500) NOT NULL DEFAULT '',
|
||||
content mediumtext,
|
||||
url varchar(500) NOT NULL DEFAULT '',
|
||||
sort int NOT NULL DEFAULT 0,
|
||||
status tinyint NOT NULL DEFAULT 1,
|
||||
create_time datetime NOT NULL,
|
||||
update_time datetime DEFAULT NULL,
|
||||
delete_time datetime DEFAULT NULL,
|
||||
PRIMARY KEY (id),
|
||||
KEY idx_tid_status (tid, status)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4`).Exec()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// 确保已有表补齐 content 字段
|
||||
_, _ = Orm.Raw("ALTER TABLE yz_cms_solution ADD COLUMN content mediumtext").Exec()
|
||||
_, err = Orm.Raw(`
|
||||
CREATE TABLE IF NOT EXISTS yz_cms_solution_category (
|
||||
id bigint unsigned NOT NULL AUTO_INCREMENT,
|
||||
tid bigint unsigned NOT NULL DEFAULT 0,
|
||||
title varchar(100) NOT NULL DEFAULT '',
|
||||
pid bigint unsigned NOT NULL DEFAULT 0,
|
||||
` + "`desc`" + ` varchar(500) NOT NULL DEFAULT '',
|
||||
sort int NOT NULL DEFAULT 0,
|
||||
status tinyint NOT NULL DEFAULT 1,
|
||||
create_time datetime NOT NULL,
|
||||
update_time datetime DEFAULT NULL,
|
||||
delete_time datetime DEFAULT NULL,
|
||||
PRIMARY KEY (id),
|
||||
KEY idx_tid_pid (tid, pid)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4`).Exec()
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
package models
|
||||
|
||||
import "time"
|
||||
|
||||
// FrontMenuDefault 全局默认前端导航项定义
|
||||
type FrontMenuDefault struct {
|
||||
Title string
|
||||
Path string
|
||||
Type int8 // 菜单类型:2 页面 / 4 单页
|
||||
Sort int
|
||||
}
|
||||
|
||||
// FrontMenuDefaults 所有租户共用的默认导航(不可删除)。
|
||||
// 以 tenant_id=0 存放于同一张菜单表,表示全局共享:
|
||||
// 租户端增删改接口都带 tenant_id 过滤,天然无法触碰这些行。
|
||||
// 路径对应官网前台路由:新闻中心 /news,其余为单页 /page/:path
|
||||
// (单页内容在 backend 端 - 单页管理 中按 path 维护)。
|
||||
var FrontMenuDefaults = []FrontMenuDefault{
|
||||
{Title: "新闻中心", Path: "/news", Type: 2, Sort: 1},
|
||||
{Title: "产品展示", Path: "/page/products", Type: 4, Sort: 2},
|
||||
{Title: "解决方案", Path: "/page/solutions", Type: 4, Sort: 3},
|
||||
{Title: "关于我们", Path: "/page/about", Type: 4, Sort: 4},
|
||||
{Title: "联系我们", Path: "/page/contact", Type: 4, Sort: 5},
|
||||
}
|
||||
|
||||
// EnsureGlobalDefaultFrontMenus 保证全局默认导航(tenant_id=0)齐全:
|
||||
// 缺失的自动新增;被误删(软删)的自动恢复;已存在(含改名)的不动。
|
||||
// 以"在库生效数量 >= 默认项总数"作为短路条件,避免改名后重复插入。
|
||||
// 供后台菜单列表与前台导航渲染前调用,任何失败均静默忽略不阻断主流程。
|
||||
func EnsureGlobalDefaultFrontMenus() {
|
||||
if Orm == nil {
|
||||
return
|
||||
}
|
||||
qs := Orm.QueryTable(new(BackendMenuFront)).Filter("tenant_id", 0)
|
||||
|
||||
activeCnt, err := qs.Filter("delete_time__isnull", true).Count()
|
||||
if err != nil || activeCnt >= int64(len(FrontMenuDefaults)) {
|
||||
return
|
||||
}
|
||||
|
||||
// 含软删记录一起取,用于恢复被误删的默认菜单
|
||||
var rows []BackendMenuFront
|
||||
if _, err := qs.All(&rows); err != nil {
|
||||
return
|
||||
}
|
||||
exist := make(map[string]*BackendMenuFront, len(rows))
|
||||
for i := range rows {
|
||||
exist[rows[i].Title] = &rows[i]
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
for _, def := range FrontMenuDefaults {
|
||||
if row, ok := exist[def.Title]; ok {
|
||||
if row.DeleteTime != nil {
|
||||
_, _ = Orm.QueryTable(new(BackendMenuFront)).
|
||||
Filter("id", row.ID).
|
||||
Update(map[string]interface{}{
|
||||
"delete_time": nil,
|
||||
"status": 1,
|
||||
"is_visible": 1,
|
||||
"update_time": now,
|
||||
})
|
||||
}
|
||||
continue
|
||||
}
|
||||
_, _ = Orm.Insert(&BackendMenuFront{
|
||||
TenantID: 0,
|
||||
Pid: 0,
|
||||
Title: def.Title,
|
||||
Path: def.Path,
|
||||
Sort: def.Sort,
|
||||
Status: 1,
|
||||
IsVisible: 1,
|
||||
Type: def.Type,
|
||||
})
|
||||
}
|
||||
}
|
||||
+11
-271
@@ -140,278 +140,18 @@ func Init(_ string) {
|
||||
)
|
||||
|
||||
// 创建全局 Ormer
|
||||
// 数据库约定(重要):
|
||||
// 1. 表结构与全局预置数据变更,一律以 SQL 文件形式放在 docs/sql/ 下由人工执行,
|
||||
// 代码中禁止自动建表 / 补列 / 补全局预置数据(本层不做任何此类操作);
|
||||
// 2. 例外:租户级默认业务数据初始化保留在业务函数中(新租户首次使用时补建,SQL 无法预置),
|
||||
// 搜索「【租户级初始化】」可定位全部位置:
|
||||
// EnsureOaDocDefaultCategories 内置文档分类(项目文档)
|
||||
// crmEnsureDefaultUnits 默认计量单位(套/台/个…)
|
||||
// crmEnsureDefaultProductCategories 默认产品分类(未分类/软件开发/软件产品/硬件产品)
|
||||
// 3. 角色体系:角色数据统一维护在 yz_system_admin_role
|
||||
// (平台角色 cid=1;全局租户角色 cid=2 且 tenant_id=0:租户管理员/租户用户),
|
||||
// 新建与调整走 SQL:docs/sql/update_role_system.sql,代码不做自动补建。
|
||||
Orm = orm.NewOrm()
|
||||
|
||||
// 补齐历史库缺失的列(已存在时 MySQL 报 duplicate column,统一忽略)
|
||||
EnsureAdminRoleTenantColumn()
|
||||
EnsureAdminRoleCustomColumn()
|
||||
EnsureTenantUserOrgColumn()
|
||||
EnsureCrmCustomerPoolColumns()
|
||||
EnsureCrmCreateUserColumn()
|
||||
EnsureCrmProjectDocColumn()
|
||||
EnsureCrmContractTable()
|
||||
EnsureCrmClueAmountColumn()
|
||||
EnsureCrmContractOurRoleColumn()
|
||||
EnsureCrmProjectProductsColumn()
|
||||
EnsureCrmPaybackTable()
|
||||
EnsureCrmPaybackRecordTable()
|
||||
EnsureCrmProductDevColumns()
|
||||
EnsureCrmUnitTable()
|
||||
EnsureErpCompanyContactMobilesColumn()
|
||||
EnsurePlatformSiteSettingTable()
|
||||
}
|
||||
|
||||
// EnsureCrmPaybackTable 回款计划表建表(CREATE TABLE IF NOT EXISTS,可重复执行;
|
||||
// 建表失败(如表已存在但结构不一致)时静默忽略,可用 sql/yz_backend_crm_payback.sql 手动修复)。
|
||||
func EnsureCrmPaybackTable() {
|
||||
sql := `CREATE TABLE IF NOT EXISTS yz_backend_crm_payback (
|
||||
id bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增主键ID',
|
||||
tenant_id varchar(64) NOT NULL COMMENT '租户ID',
|
||||
contract_id bigint(20) DEFAULT NULL COMMENT '关联合同ID(yz_backend_crm_contract.id)',
|
||||
contract_no varchar(50) NOT NULL DEFAULT '' COMMENT '合同编号',
|
||||
contract_name varchar(100) NOT NULL DEFAULT '' COMMENT '合同名称',
|
||||
customer_id bigint(20) DEFAULT NULL COMMENT '客户ID',
|
||||
customer_name varchar(128) NOT NULL DEFAULT '' COMMENT '客户名称',
|
||||
plan_type tinyint(4) NOT NULL DEFAULT '1' COMMENT '回款周期:1月度/2季度/3年度/4进度/5自定义',
|
||||
pay_method tinyint(4) NOT NULL DEFAULT '1' COMMENT '回款方式:1对公转账/2网银转账/3现金/4支票/5支付宝/6微信/7其他',
|
||||
remind_days int(11) NOT NULL DEFAULT '0' COMMENT '提前几天提醒',
|
||||
owner_user_id varchar(64) NOT NULL DEFAULT '' COMMENT '负责人用户ID',
|
||||
owner_user_name varchar(128) NOT NULL DEFAULT '' COMMENT '负责人姓名',
|
||||
contract_amount decimal(14,2) NOT NULL DEFAULT '0.00' COMMENT '合同总金额',
|
||||
total_amount decimal(14,2) NOT NULL DEFAULT '0.00' COMMENT '计划回款总额',
|
||||
received_amount decimal(14,2) NOT NULL DEFAULT '0.00' COMMENT '已回款金额',
|
||||
status tinyint(4) NOT NULL DEFAULT '1' COMMENT '状态:1进行中/2已完成',
|
||||
items text COMMENT '计划明细JSON数组',
|
||||
remark text COMMENT '备注',
|
||||
create_user_id varchar(64) NOT NULL DEFAULT '' COMMENT '创建人用户ID',
|
||||
create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
update_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
delete_time datetime DEFAULT NULL COMMENT '删除时间(软删除)',
|
||||
PRIMARY KEY (id),
|
||||
KEY idx_tenant_id (tenant_id),
|
||||
KEY idx_contract (tenant_id,contract_id),
|
||||
KEY idx_customer (tenant_id,customer_id),
|
||||
KEY idx_status (tenant_id,status),
|
||||
KEY idx_delete_time (delete_time)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='租户CRM回款计划表'`
|
||||
if _, err := Orm.Raw(sql).Exec(); err != nil {
|
||||
// 表已存在或执行失败时忽略(完整表结构见 sql/yz_backend_crm_payback.sql)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// EnsureCrmPaybackRecordTable 回款记录表建表(CREATE TABLE IF NOT EXISTS,可重复执行)。
|
||||
// 回款记录 = 回款进度流水:每次「新建回款」追加一条,以回款计划(payback_id)为先导条件。
|
||||
func EnsureCrmPaybackRecordTable() {
|
||||
sql := `CREATE TABLE IF NOT EXISTS yz_backend_crm_payback_record (
|
||||
id bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增主键ID',
|
||||
tenant_id varchar(64) NOT NULL COMMENT '租户ID',
|
||||
contract_id bigint(20) DEFAULT NULL COMMENT '关联合同ID(yz_backend_crm_contract.id)',
|
||||
payback_id bigint(20) NOT NULL DEFAULT '0' COMMENT '关联回款计划ID(yz_backend_crm_payback.id)',
|
||||
amount decimal(14,2) NOT NULL DEFAULT '0.00' COMMENT '本次回款金额',
|
||||
received_date varchar(20) NOT NULL DEFAULT '' COMMENT '回款日期 YYYY-MM-DD',
|
||||
pay_method tinyint(4) NOT NULL DEFAULT '1' COMMENT '回款方式:1对公/2网银/3现金/4支票/5支付宝/6微信/7其他',
|
||||
receipt_url varchar(512) DEFAULT NULL COMMENT '流水回执文件地址',
|
||||
receipt_name varchar(255) DEFAULT NULL COMMENT '流水回执文件名',
|
||||
remark text COMMENT '备注',
|
||||
owner_user_id varchar(64) NOT NULL DEFAULT '' COMMENT '负责人用户ID',
|
||||
owner_user_name varchar(128) NOT NULL DEFAULT '' COMMENT '负责人姓名',
|
||||
create_user_id varchar(64) NOT NULL DEFAULT '' COMMENT '创建人用户ID',
|
||||
create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
update_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
delete_time datetime DEFAULT NULL COMMENT '删除时间(软删除)',
|
||||
PRIMARY KEY (id),
|
||||
KEY idx_tenant_contract (tenant_id,contract_id),
|
||||
KEY idx_payback (tenant_id,payback_id),
|
||||
KEY idx_delete_time (delete_time)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='租户CRM回款记录表(回款进度流水)'`
|
||||
if _, err := Orm.Raw(sql).Exec(); err != nil {
|
||||
// 表已存在或执行失败时忽略
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// EnsureCrmContractOurRoleColumn 补齐合同表的我方角色字段(存量表已建时新增;
|
||||
// 旧版「合同性质 contract_nature」字段弃用但保留不动,表不存在或列已存在时忽略错误)。
|
||||
func EnsureCrmContractOurRoleColumn() {
|
||||
sql := "ALTER TABLE " + new(TenantCrmContract).TableName() +
|
||||
" ADD COLUMN our_role tinyint NOT NULL DEFAULT 2 COMMENT '我方角色:1甲方/2乙方/3丙方/4丁方'"
|
||||
_, _ = Orm.Raw(sql).Exec()
|
||||
}
|
||||
|
||||
// EnsureCrmClueAmountColumn 补齐线索表的预计金额字段(线索阶段预估金额;
|
||||
// 存量表已建时新增,表不存在或列已存在时忽略错误)。
|
||||
func EnsureCrmClueAmountColumn() {
|
||||
sql := "ALTER TABLE " + new(TenantCrmClue).TableName() +
|
||||
" ADD COLUMN amount decimal(14,2) NOT NULL DEFAULT '0.00' COMMENT '预计金额'"
|
||||
_, _ = Orm.Raw(sql).Exec()
|
||||
}
|
||||
|
||||
// EnsureCrmProjectProductsColumn 补齐项目表的产品清单字段(存量表已建时新增;
|
||||
// 表不存在或列已存在时忽略错误,可用 docs/sql/alter_backend_crm_project_products_column.sql 手动修复)。
|
||||
func EnsureCrmProjectProductsColumn() {
|
||||
sql := "ALTER TABLE " + new(TenantCrmProject).TableName() +
|
||||
" ADD COLUMN products text COMMENT '项目产品清单JSON(写入产品管理时使用)'"
|
||||
_, _ = Orm.Raw(sql).Exec()
|
||||
}
|
||||
|
||||
// EnsureCrmContractTable 合同表建表(CREATE TABLE IF NOT EXISTS,可重复执行;
|
||||
// 建表失败(如表已存在但结构不一致)时静默忽略,可用 sql/yz_backend_crm_contract.sql 手动修复)。
|
||||
func EnsureCrmContractTable() {
|
||||
sql := `CREATE TABLE IF NOT EXISTS yz_backend_crm_contract (
|
||||
id bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增主键ID',
|
||||
tenant_id varchar(64) NOT NULL COMMENT '租户ID',
|
||||
contract_no varchar(50) NOT NULL DEFAULT '' COMMENT '合同编号',
|
||||
contract_name varchar(100) NOT NULL COMMENT '合同名称',
|
||||
contract_category varchar(20) NOT NULL DEFAULT '' COMMENT '合同分类:1开发/2服务/3销售/4租赁/5采购/6运维/7咨询/8其他',
|
||||
our_role tinyint(4) NOT NULL DEFAULT '2' COMMENT '我方角色:1甲方/2乙方/3丙方/4丁方',
|
||||
party_count tinyint(4) NOT NULL DEFAULT '2' COMMENT '合同形式:2/3/4方',
|
||||
project_id bigint(20) DEFAULT NULL COMMENT '关联项目ID,空=无头合同',
|
||||
project_name varchar(100) NOT NULL DEFAULT '' COMMENT '项目名称',
|
||||
owner_user_id varchar(64) NOT NULL DEFAULT '' COMMENT '项目负责人用户ID',
|
||||
owner_user_name varchar(128) NOT NULL DEFAULT '' COMMENT '项目负责人姓名',
|
||||
sign_date date DEFAULT NULL COMMENT '签订日期',
|
||||
effective_date date DEFAULT NULL COMMENT '生效日期',
|
||||
expire_date date DEFAULT NULL COMMENT '结束日期',
|
||||
parties text COMMENT '各方签约主体JSON',
|
||||
products text COMMENT '产品清单JSON',
|
||||
hardware_amount decimal(14,2) NOT NULL DEFAULT '0.00' COMMENT '硬件部分金额',
|
||||
software_amount decimal(14,2) NOT NULL DEFAULT '0.00' COMMENT '软件部分金额',
|
||||
other_amount decimal(14,2) NOT NULL DEFAULT '0.00' COMMENT '其他部分金额',
|
||||
total_amount decimal(14,2) NOT NULL DEFAULT '0.00' COMMENT '合同总金额',
|
||||
total_cost decimal(14,2) NOT NULL DEFAULT '0.00' COMMENT '产品总成本',
|
||||
total_profit decimal(14,2) NOT NULL DEFAULT '0.00' COMMENT '合同总利润',
|
||||
status tinyint(4) NOT NULL DEFAULT '1' COMMENT '状态:1草稿/2已完成/3已作废/4履约中/5异常',
|
||||
step tinyint(4) NOT NULL DEFAULT '1' COMMENT '进度步骤:1合同信息/2产品清单',
|
||||
remark text COMMENT '备注',
|
||||
create_user_id varchar(64) NOT NULL DEFAULT '' COMMENT '创建人用户ID',
|
||||
create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
update_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
delete_time datetime DEFAULT NULL COMMENT '删除时间(软删除)',
|
||||
PRIMARY KEY (id),
|
||||
KEY idx_tenant_id (tenant_id),
|
||||
KEY idx_contract_name (tenant_id,contract_name),
|
||||
KEY idx_contract_no (tenant_id,contract_no),
|
||||
KEY idx_project (tenant_id,project_id),
|
||||
KEY idx_status (tenant_id,status),
|
||||
KEY idx_delete_time (delete_time)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='租户CRM合同表'`
|
||||
if _, err := Orm.Raw(sql).Exec(); err != nil {
|
||||
// 表已存在或执行失败时忽略(完整表结构见 sql/yz_backend_crm_contract.sql)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// EnsureCrmProjectDocColumn 补齐项目表的文档库文件夹分类字段(项目文档绑定 OA 文档库)。
|
||||
// 表不存在或列已存在时忽略错误。
|
||||
func EnsureCrmProjectDocColumn() {
|
||||
sql := "ALTER TABLE " + new(TenantCrmProject).TableName() +
|
||||
" ADD COLUMN doc_category_id bigint unsigned NOT NULL DEFAULT 0 COMMENT '关联OA文档库项目文件夹分类ID'"
|
||||
_, _ = Orm.Raw(sql).Exec()
|
||||
}
|
||||
|
||||
// EnsureCrmUnitTable 计量单位表建表(CREATE TABLE IF NOT EXISTS,可重复执行;
|
||||
// 建表失败(如表已存在但结构不一致)时静默忽略,可用 docs/sql/create_backend_crm_unit.sql 手动修复)。
|
||||
// 默认单位由控制器在租户首次访问单位列表时写入(需要 tenant_id,无法在启动时预置)。
|
||||
func EnsureCrmUnitTable() {
|
||||
sql := `CREATE TABLE IF NOT EXISTS yz_backend_crm_unit (
|
||||
id bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增主键ID',
|
||||
tenant_id varchar(64) NOT NULL COMMENT '租户ID',
|
||||
name varchar(20) NOT NULL COMMENT '单位名称,如:套 / 台 / 个',
|
||||
code varchar(50) NOT NULL DEFAULT '' COMMENT '单位编码(选填),如:SET / PCS',
|
||||
sort int(11) NOT NULL DEFAULT '0' COMMENT '排序(升序,小的在前)',
|
||||
status tinyint(4) NOT NULL DEFAULT '1' COMMENT '状态:1启用 / 0停用',
|
||||
remark varchar(255) NOT NULL DEFAULT '' COMMENT '备注',
|
||||
create_user_id varchar(64) NOT NULL DEFAULT '' COMMENT '创建人用户ID',
|
||||
create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
update_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
delete_time datetime DEFAULT NULL COMMENT '删除时间(软删除)',
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_tenant_name (tenant_id,name),
|
||||
KEY idx_tenant_id (tenant_id),
|
||||
KEY idx_status (tenant_id,status),
|
||||
KEY idx_delete_time (delete_time)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='租户CRM计量单位表'`
|
||||
if _, err := Orm.Raw(sql).Exec(); err != nil {
|
||||
// 表已存在或执行失败时忽略(完整表结构见 docs/sql/create_backend_crm_unit.sql)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// EnsureErpCompanyContactMobilesColumn 补齐公司联系人表(yz_backend_contact_company)的多手机号字段 mobiles。
|
||||
// 该表为客户/供应商正式联系人的共用库(项目联系人选择、通讯录、客户-供应商转化等均读取),
|
||||
// 早期多手机号升级遗漏了此表,缺列会导致查询报错(前端表现为「联系人不显示」)。
|
||||
// 表不存在或列已存在时忽略错误,可用 docs/sql/alter_contact_mobiles.sql 手动修复。
|
||||
func EnsureErpCompanyContactMobilesColumn() {
|
||||
table := new(ErpCompanyContact).TableName()
|
||||
sql := fmt.Sprintf("ALTER TABLE %s ADD COLUMN mobiles text NULL COMMENT '手机号 JSON 数组(最多5个),phone 保留座机语义'", table)
|
||||
if _, err := Orm.Raw(sql).Exec(); err != nil {
|
||||
// 列已存在或表不存在时忽略
|
||||
return
|
||||
}
|
||||
// 历史数据回填:原 phone 中的号码作为第一个手机号(仅在 mobiles 为空时)
|
||||
_, _ = Orm.Raw("UPDATE " + table +
|
||||
" SET mobiles = JSON_ARRAY(phone) WHERE (mobiles IS NULL OR mobiles = '') AND phone IS NOT NULL AND phone <> ''").Exec()
|
||||
}
|
||||
|
||||
// EnsureCrmProductDevColumns 补齐产品表的软件开发字段(line_type / dev_unit_price / dev_mode / dev_tree);
|
||||
// 表不存在或列已存在时忽略错误,可用 docs/sql/alter_backend_crm_product_dev_columns.sql 手动修复。
|
||||
func EnsureCrmProductDevColumns() {
|
||||
table := new(TenantCrmProduct).TableName()
|
||||
cols := []string{
|
||||
"line_type varchar(20) NOT NULL DEFAULT 'product' COMMENT '行类型:product成品/dev软件开发'",
|
||||
"dev_unit_price decimal(14,2) NOT NULL DEFAULT '0.00' COMMENT '软件开发统一人天单价'",
|
||||
"dev_mode varchar(20) NOT NULL DEFAULT 'self' COMMENT '软件开发研发方式:self自行研发/outsource外协研发'",
|
||||
"dev_tree text COMMENT '软件开发模块树JSON(叶子人天×统一人天单价)'",
|
||||
}
|
||||
for _, col := range cols {
|
||||
sql := fmt.Sprintf("ALTER TABLE %s ADD COLUMN %s", table, col)
|
||||
if _, err := Orm.Raw(sql).Exec(); err != nil {
|
||||
// 列已存在或表不存在时忽略
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// EnsureCrmCreateUserColumn 补齐客户/供应商的创建人字段(用于删除权限校验)。
|
||||
// 同样兼容历史表名差异,表不存在或列已存在时忽略错误。
|
||||
func EnsureCrmCreateUserColumn() {
|
||||
tables := []string{
|
||||
new(TenantCrmCustomer).TableName(), "yz_tenant_crm_customer",
|
||||
new(TenantCrmSupplier).TableName(), "yz_tenant_crm_supplier",
|
||||
}
|
||||
for _, table := range tables {
|
||||
sql := "ALTER TABLE " + table +
|
||||
" ADD COLUMN create_user_id varchar(64) NOT NULL DEFAULT '' COMMENT '创建人用户ID,用于删除权限校验'"
|
||||
if _, err := Orm.Raw(sql).Exec(); err != nil {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// EnsureCrmCustomerPoolColumns 补齐客户公海相关字段。
|
||||
// 兼容历史表名差异:模型 TableName 为 yz_backend_erp_customer,初始化 SQL 中为 yz_tenant_crm_customer,
|
||||
// 对两个候选表名都执行 ALTER(表不存在或列已存在时忽略错误)。
|
||||
func EnsureCrmCustomerPoolColumns() {
|
||||
candidates := []string{
|
||||
new(TenantCrmCustomer).TableName(),
|
||||
"yz_tenant_crm_customer",
|
||||
}
|
||||
cols := []string{
|
||||
"in_pool tinyint NOT NULL DEFAULT 0 COMMENT '是否在公海:0-否 1-是'",
|
||||
"owner_user_id varchar(64) NOT NULL DEFAULT '' COMMENT '负责人ID,公海中为空'",
|
||||
"owner_user_name varchar(128) NOT NULL DEFAULT '' COMMENT '负责人姓名'",
|
||||
"last_owner_name varchar(128) NOT NULL DEFAULT '' COMMENT '移入公海前的原负责人'",
|
||||
"pool_in_time datetime DEFAULT NULL COMMENT '进入公海时间'",
|
||||
"pool_reason varchar(200) NOT NULL DEFAULT '' COMMENT '移入公海原因'",
|
||||
}
|
||||
for _, table := range candidates {
|
||||
for _, col := range cols {
|
||||
sql := fmt.Sprintf("ALTER TABLE %s ADD COLUMN %s", table, col)
|
||||
if _, err := Orm.Raw(sql).Exec(); err != nil {
|
||||
// 列已存在或表不存在时忽略
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-151
@@ -209,161 +209,12 @@ func DocTypeByExt(ext string) int8 {
|
||||
return DocTypeOther
|
||||
}
|
||||
|
||||
var oaDocumentTableOnce sync.Once
|
||||
|
||||
// EnsureOaDocumentTables 首次访问文档接口时自动建表(若不存在)。
|
||||
// 与通知公告/日程保持一致的运行期自愈策略,避免新功能上线还需手工执行建表脚本。
|
||||
func EnsureOaDocumentTables() error {
|
||||
if Orm == nil {
|
||||
return nil
|
||||
}
|
||||
var err error
|
||||
oaDocumentTableOnce.Do(func() {
|
||||
_, err = Orm.Raw(`
|
||||
CREATE TABLE IF NOT EXISTS yz_backend_oa_doc_category (
|
||||
id bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
tid int NOT NULL DEFAULT 0 COMMENT '租户ID',
|
||||
user_id bigint unsigned NOT NULL DEFAULT 0 COMMENT '归属用户ID 0-租户级(共享分类) >0-该用户的个人分类',
|
||||
parent_id bigint unsigned NOT NULL DEFAULT 0 COMMENT '父级ID,0为一级分类',
|
||||
name varchar(128) NOT NULL DEFAULT '' COMMENT '分类名称',
|
||||
sort int NOT NULL DEFAULT 0 COMMENT '排序,越小越靠前',
|
||||
remark varchar(255) NOT NULL DEFAULT '' COMMENT '备注',
|
||||
is_system tinyint NOT NULL DEFAULT 0 COMMENT '是否系统内置分类 0-否(可删除) 1-是(不可删除)',
|
||||
scope varchar(16) NOT NULL DEFAULT 'shared' COMMENT '所属范围 personal-个人文档 shared-共享文档',
|
||||
is_deleted tinyint NOT NULL DEFAULT 0 COMMENT '是否删除 0-否 1-是',
|
||||
create_time datetime DEFAULT NULL COMMENT '创建时间',
|
||||
update_time datetime DEFAULT NULL COMMENT '更新时间',
|
||||
delete_time datetime DEFAULT NULL COMMENT '删除时间',
|
||||
PRIMARY KEY (id),
|
||||
KEY idx_tid_parent (tid, parent_id),
|
||||
KEY idx_tid_scope_user (tid, scope, user_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='OA文档分类表'`).Exec()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, err = Orm.Raw(`
|
||||
CREATE TABLE IF NOT EXISTS yz_backend_oa_doc (
|
||||
id bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
tid int NOT NULL DEFAULT 0 COMMENT '租户ID',
|
||||
category_id bigint unsigned NOT NULL DEFAULT 0 COMMENT '所属分类ID,0为未分类',
|
||||
title varchar(255) NOT NULL DEFAULT '' COMMENT '文档标题',
|
||||
file_id bigint unsigned NOT NULL DEFAULT 0 COMMENT '关联附件ID(yz_system_files)',
|
||||
file_url varchar(512) NOT NULL DEFAULT '' COMMENT '附件访问地址',
|
||||
file_name varchar(255) NOT NULL DEFAULT '' COMMENT '原始文件名',
|
||||
ext varchar(16) NOT NULL DEFAULT '' COMMENT '扩展名',
|
||||
size bigint unsigned NOT NULL DEFAULT 0 COMMENT '文件大小(字节)',
|
||||
doc_type tinyint NOT NULL DEFAULT 0 COMMENT '类型 0-其他 1-文档 2-表格 3-演示 4-PDF 5-图片 6-压缩包 7-视频 8-音频',
|
||||
tags varchar(255) NOT NULL DEFAULT '' COMMENT '标签,逗号分隔',
|
||||
summary varchar(1000) NOT NULL DEFAULT '' COMMENT '摘要/描述',
|
||||
content longtext NULL COMMENT '在线制作文档的编辑源(富文本HTML),仅用于再次编辑',
|
||||
status tinyint NOT NULL DEFAULT 0 COMMENT '状态 0-草稿 1-已发布 2-已归档',
|
||||
version int NOT NULL DEFAULT 1 COMMENT '版本号',
|
||||
is_star tinyint NOT NULL DEFAULT 0 COMMENT '是否收藏 0-否 1-是',
|
||||
owner_id bigint unsigned NOT NULL DEFAULT 0 COMMENT '负责人ID',
|
||||
owner_name varchar(100) NOT NULL DEFAULT '' COMMENT '负责人姓名',
|
||||
view_count int NOT NULL DEFAULT 0 COMMENT '查看次数',
|
||||
download_count int NOT NULL DEFAULT 0 COMMENT '下载次数',
|
||||
creator_id bigint unsigned NOT NULL DEFAULT 0 COMMENT '创建人ID',
|
||||
creator_name varchar(100) NOT NULL DEFAULT '' COMMENT '创建人姓名',
|
||||
visibility tinyint NOT NULL DEFAULT 0 COMMENT '可见性 0-租户公开 1-私密(仅创建者与被共享者)',
|
||||
is_deleted tinyint NOT NULL DEFAULT 0 COMMENT '是否删除 0-否 1-是',
|
||||
create_time datetime DEFAULT NULL COMMENT '创建时间',
|
||||
update_time datetime DEFAULT NULL COMMENT '更新时间',
|
||||
delete_time datetime DEFAULT NULL COMMENT '删除时间',
|
||||
PRIMARY KEY (id),
|
||||
KEY idx_tid_cate (tid, category_id, is_deleted),
|
||||
KEY idx_tid_status (tid, status, is_deleted),
|
||||
KEY idx_tid_title (tid, title)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='OA文档表'`).Exec()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, err = Orm.Raw(`
|
||||
CREATE TABLE IF NOT EXISTS yz_backend_oa_doc_link (
|
||||
id bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
tid int NOT NULL DEFAULT 0 COMMENT '租户ID',
|
||||
source_id bigint unsigned NOT NULL DEFAULT 0 COMMENT '源文档ID',
|
||||
target_id bigint unsigned NOT NULL DEFAULT 0 COMMENT '目标文档ID',
|
||||
relation varchar(32) NOT NULL DEFAULT 'related' COMMENT '关系 reference-引用 related-相关 version-版本 belong-从属',
|
||||
remark varchar(255) NOT NULL DEFAULT '' COMMENT '关系说明',
|
||||
creator_id bigint unsigned NOT NULL DEFAULT 0 COMMENT '创建人ID',
|
||||
create_time datetime DEFAULT NULL COMMENT '创建时间',
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_doc_pair (tid, source_id, target_id, relation),
|
||||
KEY idx_tid_source (tid, source_id),
|
||||
KEY idx_tid_target (tid, target_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='OA文档关联表'`).Exec()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 历史库补齐 visibility 列(已存在时 MySQL 报 duplicate column,忽略)
|
||||
_, _ = Orm.Raw(`
|
||||
ALTER TABLE yz_backend_oa_doc
|
||||
ADD COLUMN visibility tinyint NOT NULL DEFAULT 0 COMMENT '可见性 0-租户公开 1-私密(仅创建者与被共享者)'
|
||||
`).Exec()
|
||||
|
||||
// 历史库补齐在线文档编辑源列 content(在线制作功能上线前已建表)
|
||||
_, _ = Orm.Raw(`
|
||||
ALTER TABLE yz_backend_oa_doc
|
||||
ADD COLUMN content longtext NULL COMMENT '在线制作文档的编辑源(富文本HTML),仅用于再次编辑'
|
||||
`).Exec()
|
||||
|
||||
// 历史库补齐分类表的 is_system 列(内置分类特性上线前已建表)
|
||||
_, _ = Orm.Raw(`
|
||||
ALTER TABLE yz_backend_oa_doc_category
|
||||
ADD COLUMN is_system tinyint NOT NULL DEFAULT 0 COMMENT '是否系统内置分类 0-否(可删除) 1-是(不可删除)'
|
||||
`).Exec()
|
||||
|
||||
// 历史库补齐分类表的 scope 列:既有分类默认归入共享文档,保证升级后仍可见
|
||||
_, _ = Orm.Raw(`
|
||||
ALTER TABLE yz_backend_oa_doc_category
|
||||
ADD COLUMN scope varchar(16) NOT NULL DEFAULT 'shared' COMMENT '所属范围 personal-个人文档 shared-共享文档'
|
||||
`).Exec()
|
||||
|
||||
// 历史库补齐分类表的 user_id 列:既有分类默认归租户级(共享分类)
|
||||
_, _ = Orm.Raw(`
|
||||
ALTER TABLE yz_backend_oa_doc_category
|
||||
ADD COLUMN user_id bigint unsigned NOT NULL DEFAULT 0 COMMENT '归属用户ID 0-租户级(共享分类) >0-该用户的个人分类'
|
||||
`).Exec()
|
||||
|
||||
_, err = Orm.Raw(`
|
||||
CREATE TABLE IF NOT EXISTS yz_backend_oa_doc_share (
|
||||
id bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
tid int NOT NULL DEFAULT 0 COMMENT '租户ID',
|
||||
doc_id bigint unsigned NOT NULL DEFAULT 0 COMMENT '文档ID',
|
||||
share_type tinyint NOT NULL DEFAULT 0 COMMENT '共享类型 0-用户(target_id=uid) 1-部门(target_id=org_id,含子部门)',
|
||||
target_id bigint unsigned NOT NULL DEFAULT 0 COMMENT '共享目标ID',
|
||||
creator_id bigint unsigned NOT NULL DEFAULT 0 COMMENT '授权人ID',
|
||||
create_time datetime DEFAULT NULL COMMENT '创建时间',
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_doc_target (tid, doc_id, share_type, target_id),
|
||||
KEY idx_tid_doc (tid, doc_id),
|
||||
KEY idx_target (tid, share_type, target_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='OA文档共享表'`).Exec()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 双空间模型:兼容存量数据,将不在 (shared, personal) 范围内的分类统一归为共享空间
|
||||
// (user_id 置 0、scope 置 shared),避免历史脏数据导致分类不可见。
|
||||
// 注意:不再把 personal 并入 shared,私密空间需保留各自的 user_id 隔离。
|
||||
_, _ = Orm.Raw(`
|
||||
UPDATE yz_backend_oa_doc_category
|
||||
SET user_id = 0, scope = 'shared'
|
||||
WHERE scope NOT IN ('shared', 'personal') AND is_deleted = 0
|
||||
`).Exec()
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// oaDocDefaultCategoryOnce 记录已完成内置分类补建的租户,避免每次请求重复查库。
|
||||
// key 为 tid(int),value 为 struct{}{};补建失败时不写缓存,下次请求继续重试。
|
||||
var oaDocDefaultCategoryOnce sync.Map
|
||||
|
||||
// EnsureOaDocDefaultCategories 保证指定租户的系统内置分类齐全(当前为「项目文档」)。
|
||||
// 【租户级初始化】新租户首次使用时补建默认业务数据(非表结构/全局预置数据;结构变更须走 docs/sql/ SQL 文件)。
|
||||
//
|
||||
// 租户隔离:内置分类是真实数据行,每个租户各自持有一份(tid 不同),
|
||||
// 首次访问文档接口时自动补建,租户之间互不可见、互不影响。
|
||||
@@ -374,7 +225,7 @@ var oaDocDefaultCategoryOnce sync.Map
|
||||
// 「未分类」不落库:它是虚拟分类,文档 category_id = 0 即表示未分类,因此天然不可删除;
|
||||
// 同时在 services 层把「未分类」「项目文档」列为保留名,禁止新建同名分类与之混淆。
|
||||
//
|
||||
// 任何失败均静默忽略,不阻断主流程(与 EnsureDefaultTenantRoles 保持一致)。
|
||||
// 任何失败均静默忽略,不阻断主流程(与其它租户级初始化保持一致)。
|
||||
func EnsureOaDocDefaultCategories(tid int) {
|
||||
if Orm == nil || tid <= 0 {
|
||||
return
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -32,38 +31,4 @@ func (m *OaNotice) TableName() string {
|
||||
return "yz_backend_oa_notice"
|
||||
}
|
||||
|
||||
var oaNoticeTableOnce sync.Once
|
||||
|
||||
// EnsureOaNoticeTable 首次访问通知公告接口时自动建表(若不存在)。
|
||||
// 与日程表保持一致的"运行时自愈"策略,避免新功能上线还要手工执行建表脚本。
|
||||
func EnsureOaNoticeTable() error {
|
||||
if Orm == nil {
|
||||
return nil
|
||||
}
|
||||
var err error
|
||||
oaNoticeTableOnce.Do(func() {
|
||||
_, err = Orm.Raw(`
|
||||
CREATE TABLE IF NOT EXISTS yz_backend_oa_notice (
|
||||
id bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
tid int NOT NULL DEFAULT 0 COMMENT '租户ID',
|
||||
title varchar(255) NOT NULL DEFAULT '' COMMENT '公告标题',
|
||||
content text COMMENT '公告内容',
|
||||
notice_type tinyint NOT NULL DEFAULT 0 COMMENT '类型 0-通知 1-公告 2-活动',
|
||||
level tinyint NOT NULL DEFAULT 0 COMMENT '紧急程度 0-普通 1-重要 2-紧急',
|
||||
status tinyint NOT NULL DEFAULT 0 COMMENT '状态 0-草稿 1-已发布 2-已下架',
|
||||
is_top tinyint NOT NULL DEFAULT 0 COMMENT '是否置顶 0-否 1-是',
|
||||
publish_time datetime DEFAULT NULL COMMENT '发布时间',
|
||||
publisher_id bigint unsigned NOT NULL DEFAULT 0 COMMENT '发布人ID',
|
||||
publisher_name varchar(100) NOT NULL DEFAULT '' COMMENT '发布人姓名',
|
||||
read_count int NOT NULL DEFAULT 0 COMMENT '阅读次数',
|
||||
is_deleted tinyint NOT NULL DEFAULT 0 COMMENT '是否删除 0-否 1-是',
|
||||
create_time datetime DEFAULT NULL COMMENT '创建时间',
|
||||
update_time datetime DEFAULT NULL COMMENT '更新时间',
|
||||
delete_time datetime DEFAULT NULL COMMENT '删除时间',
|
||||
PRIMARY KEY (id),
|
||||
KEY idx_tid_status (tid, status),
|
||||
KEY idx_tid_top (tid, is_top)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='OA通知公告表'`).Exec()
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -48,30 +47,4 @@ func (m *OaSchedule) TableName() string {
|
||||
return "yz_backend_oa_schedule"
|
||||
}
|
||||
|
||||
var oaScheduleCarryColumnsOnce sync.Once
|
||||
|
||||
// EnsureOaScheduleCarryColumns 为日程表补齐"顺延到下一天"与"相关图片"所需字段。
|
||||
// 历史库可能早于该特性建表,这里在首次访问日程接口时自动补列,
|
||||
// 字段已存在时 MySQL 会报 duplicate column,统一忽略,不阻断主流程。
|
||||
func EnsureOaScheduleCarryColumns() error {
|
||||
if Orm == nil {
|
||||
return nil
|
||||
}
|
||||
var err error
|
||||
oaScheduleCarryColumnsOnce.Do(func() {
|
||||
alters := []string{
|
||||
"ALTER TABLE yz_backend_oa_schedule ADD COLUMN source_id bigint unsigned NOT NULL DEFAULT 0 COMMENT '顺延来源日程ID 0-原始日程'",
|
||||
"ALTER TABLE yz_backend_oa_schedule ADD COLUMN carry_over tinyint NOT NULL DEFAULT 0 COMMENT '是否已顺延到下一天 0-否 1-是'",
|
||||
"ALTER TABLE yz_backend_oa_schedule ADD COLUMN carry_count int NOT NULL DEFAULT 0 COMMENT '累计顺延次数'",
|
||||
"ALTER TABLE yz_backend_oa_schedule ADD COLUMN carry_to_date varchar(10) NOT NULL DEFAULT '' COMMENT '顺延目标日期(YYYY-MM-DD)'",
|
||||
"ALTER TABLE yz_backend_oa_schedule ADD COLUMN images text NULL COMMENT '相关图片JSON数组 [{id,url,name}]'",
|
||||
"ALTER TABLE yz_backend_oa_schedule ADD COLUMN reminder_id bigint unsigned NOT NULL DEFAULT 0 COMMENT '绑定的提醒任务ID(对应yz_backend_schedule.id) 0-无提醒'",
|
||||
}
|
||||
for _, sql := range alters {
|
||||
if _, e := Orm.Raw(sql).Exec(); e != nil {
|
||||
err = e
|
||||
}
|
||||
}
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -155,135 +155,4 @@ func GetPlatformSiteSettingRows() []PlatformSiteSetting {
|
||||
return rows
|
||||
}
|
||||
|
||||
// EnsurePlatformSiteSettingDefaults 补齐已登记但未入库的设置项(缺失时插入空值行)
|
||||
// 注意:须用库中实际行判断,不能用 GetPlatformSiteTagMap —— 它预置了所有默认 tag 的 key,
|
||||
// 会导致「已存在」判断永远成立、一行都插不进去。
|
||||
func EnsurePlatformSiteSettingDefaults() {
|
||||
exist := map[string]bool{}
|
||||
for _, r := range GetPlatformSiteSettingRows() {
|
||||
if r.Tag != "" {
|
||||
exist[r.Tag] = true
|
||||
}
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
for _, d := range PlatformSiteSettingDefs {
|
||||
if exist[d.Tag] {
|
||||
continue
|
||||
}
|
||||
if _, err := Orm.Insert(&PlatformSiteSetting{
|
||||
Title: d.Title,
|
||||
Tag: d.Tag,
|
||||
Value: "",
|
||||
Remark: d.Remark,
|
||||
UpdateTime: &now,
|
||||
}); err != nil {
|
||||
continue
|
||||
}
|
||||
exist[d.Tag] = true
|
||||
}
|
||||
}
|
||||
|
||||
// EnsurePlatformSiteSettingTable 建表(通用键值结构,CREATE TABLE IF NOT EXISTS,可重复执行)。
|
||||
// 若检测到旧宽表结构(含 logo_dark 列),先把历史数据迁移为键值行,再补齐默认项。
|
||||
func EnsurePlatformSiteSettingTable() {
|
||||
table := new(PlatformSiteSetting).TableName()
|
||||
sql := `CREATE TABLE IF NOT EXISTS ` + table + ` (
|
||||
id bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增主键ID',
|
||||
title varchar(128) NOT NULL DEFAULT '' COMMENT '设置项名称',
|
||||
value text COMMENT '设置项的值',
|
||||
tag varchar(64) NOT NULL DEFAULT '' COMMENT '标签名,模板中写作 {yz:tag}',
|
||||
remark varchar(255) NOT NULL DEFAULT '' COMMENT '备注/填写说明',
|
||||
create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
update_time datetime DEFAULT NULL COMMENT '更新时间',
|
||||
delete_time datetime DEFAULT NULL COMMENT '删除时间(软删除)',
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_tag (tag, delete_time),
|
||||
KEY idx_delete_time (delete_time)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='平台官网站点设置表(通用键值结构)'`
|
||||
if _, err := Orm.Raw(sql).Exec(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 旧宽表升级场景:CREATE TABLE IF NOT EXISTS 不会改已有表,需补齐通用结构列
|
||||
ensurePlatformSiteSettingColumns(table)
|
||||
|
||||
if hasTableColumn(table, "logo_dark") {
|
||||
migrateLegacyPlatformSiteSetting(table)
|
||||
}
|
||||
EnsurePlatformSiteSettingDefaults()
|
||||
}
|
||||
|
||||
// ensurePlatformSiteSettingColumns 补齐通用结构列(列缺失时新增,已存在则忽略)
|
||||
func ensurePlatformSiteSettingColumns(table string) {
|
||||
cols := []struct{ name, def string }{
|
||||
{"title", "varchar(128) NOT NULL DEFAULT '' COMMENT '设置项名称'"},
|
||||
{"value", "text COMMENT '设置项的值'"},
|
||||
{"tag", "varchar(64) NOT NULL DEFAULT '' COMMENT '标签名,模板中写作 {yz:tag}'"},
|
||||
{"remark", "varchar(255) NOT NULL DEFAULT '' COMMENT '备注/填写说明'"},
|
||||
}
|
||||
for _, c := range cols {
|
||||
if hasTableColumn(table, c.name) {
|
||||
continue
|
||||
}
|
||||
_, _ = Orm.Raw("ALTER TABLE " + table + " ADD COLUMN " + c.def).Exec()
|
||||
}
|
||||
}
|
||||
|
||||
// hasTableColumn 判断表是否存在指定列(表不存在或查询异常时返回 false)
|
||||
func hasTableColumn(table, column string) bool {
|
||||
var cnt int64
|
||||
err := Orm.Raw(
|
||||
"SELECT COUNT(1) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? AND COLUMN_NAME = ?",
|
||||
table, column,
|
||||
).QueryRow(&cnt)
|
||||
return err == nil && cnt > 0
|
||||
}
|
||||
|
||||
// migrateLegacyPlatformSiteSetting 旧宽表结构迁移:
|
||||
// 旧列为 sitename/logo_dark/logo_light/icp/police_icp/copyright/privacy_policy/terms_of_service,
|
||||
// 迁移时仅在目标标签行为空的情况下写入,避免覆盖新配置。
|
||||
func migrateLegacyPlatformSiteSetting(table string) {
|
||||
type legacyRow struct {
|
||||
Sitename string
|
||||
LogoDark string
|
||||
LogoLight string
|
||||
Icp string
|
||||
PoliceIcp string
|
||||
Copyright string
|
||||
Privacy string
|
||||
Terms string
|
||||
Companyname string
|
||||
}
|
||||
var rows []legacyRow
|
||||
if _, err := Orm.Raw(
|
||||
"SELECT IFNULL(sitename,'') sitename, IFNULL(logo_dark,'') logo_dark, IFNULL(logo_light,'') logo_light," +
|
||||
" IFNULL(icp,'') icp, IFNULL(police_icp,'') police_icp, IFNULL(copyright,'') copyright," +
|
||||
" IFNULL(privacy_policy,'') privacy_policy, IFNULL(terms_of_service,'') terms_of_service" +
|
||||
" FROM " + table + " WHERE delete_time IS NULL ORDER BY id LIMIT 1").QueryRows(&rows); err != nil {
|
||||
return
|
||||
}
|
||||
if len(rows) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
old := rows[0]
|
||||
current := GetPlatformSiteTagMap()
|
||||
pending := map[string]string{
|
||||
"sitename": old.Sitename,
|
||||
"logo": old.LogoDark,
|
||||
"logow": old.LogoLight,
|
||||
"icp": old.Icp,
|
||||
"policeicp": old.PoliceIcp,
|
||||
"copyright": old.Copyright,
|
||||
"privacy": old.Privacy,
|
||||
"terms": old.Terms,
|
||||
}
|
||||
for tag, val := range pending {
|
||||
if val == "" || strings.TrimSpace(current[tag]) != "" {
|
||||
continue
|
||||
}
|
||||
current[tag] = val
|
||||
}
|
||||
_ = SavePlatformSiteSettings(pending)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -48,34 +47,4 @@ const (
|
||||
FileScopeUser = "user" // 用户个人文件
|
||||
)
|
||||
|
||||
var systemFileStorageColumnsOnce sync.Once
|
||||
|
||||
// EnsureSystemFileStorageColumns 为附件表补齐文件存储分层改造所需字段。
|
||||
// 历史库可能早于该特性建表,字段/索引已存在时 MySQL 报 duplicate,统一忽略,不阻断主流程。
|
||||
// 写入 yz_system_files 之前必须调用,否则 INSERT 会因 Unknown column 失败。
|
||||
func EnsureSystemFileStorageColumns() {
|
||||
if Orm == nil {
|
||||
return
|
||||
}
|
||||
systemFileStorageColumnsOnce.Do(func() {
|
||||
alters := []string{
|
||||
"ALTER TABLE yz_system_files ADD COLUMN source varchar(16) NOT NULL DEFAULT 'backend' COMMENT '来源端: backend-租户后台 platform-平台端'",
|
||||
"ALTER TABLE yz_system_files ADD COLUMN scope varchar(16) NOT NULL DEFAULT 'tenant' COMMENT '归属: tenant-租户共享 user-用户个人'",
|
||||
"ALTER TABLE yz_system_files ADD COLUMN storage varchar(16) NOT NULL DEFAULT '' COMMENT '存储类型: local/qiniu'",
|
||||
"ALTER TABLE yz_system_files ADD COLUMN object_key varchar(512) NOT NULL DEFAULT '' COMMENT '存储相对路径(不含域名)'",
|
||||
}
|
||||
for _, sql := range alters {
|
||||
_, _ = Orm.Raw(sql).Exec()
|
||||
}
|
||||
// 索引:MD5 精确去重 + 归属列表过滤
|
||||
// md5 在部分历史库中是 TEXT 类型,MySQL 不允许对 TEXT/BLOB 建整列索引,
|
||||
// 故使用前缀长度 md5(32)(MD5 十六进制串固定 32 字符)。
|
||||
indexes := []string{
|
||||
"ALTER TABLE yz_system_files ADD KEY idx_file_dedup (source, scope, tid, tuid, md5(32))",
|
||||
"ALTER TABLE yz_system_files ADD KEY idx_file_owner (source, tid, scope, tuid, delete_time)",
|
||||
}
|
||||
for _, sql := range indexes {
|
||||
_, _ = Orm.Raw(sql).Exec()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,24 +1,33 @@
|
||||
package models
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// SystemReminderList 站内信消息列表表 yz_system_reminderlist
|
||||
type SystemReminderList struct {
|
||||
ID uint64 `orm:"column(id);pk;auto" json:"id"`
|
||||
Title string `orm:"column(title);size(255)" json:"title"`
|
||||
Content string `orm:"column(content);type(text)" json:"content"`
|
||||
SenderID uint64 `orm:"column(sender_id);default(0)" json:"sender_id"`
|
||||
SenderType string `orm:"column(sender_type);size(32);default('system')" json:"sender_type"` // system, platform, tenant
|
||||
ReceiverID uint64 `orm:"column(receiver_id)" json:"receiver_id"`
|
||||
ReceiverType string `orm:"column(receiver_type);size(32)" json:"receiver_type"` // platform, tenant
|
||||
IsRead int8 `orm:"column(is_read);default(0)" json:"is_read"` // 0-未读, 1-已读
|
||||
ReadTime *time.Time `orm:"column(read_time);type(datetime);null" json:"read_time"`
|
||||
CreateTime *time.Time `orm:"column(create_time);type(datetime);null" json:"create_time"`
|
||||
DeleteTime *time.Time `orm:"column(delete_time);type(datetime);null" json:"delete_time"`
|
||||
BatchID string `orm:"column(batch_id);size(64);default('')" json:"batch_id"`
|
||||
TargetType string `orm:"column(target_type);size(32);default('')" json:"target_type"`
|
||||
TargetRoleID uint64 `orm:"column(target_role_id);default(0)" json:"target_role_id"`
|
||||
TargetTenantID uint64 `orm:"column(target_tenant_id);default(0)" json:"target_tenant_id"`
|
||||
ID uint64 `orm:"column(id);pk;auto" json:"id"`
|
||||
Title string `orm:"column(title);size(255)" json:"title"`
|
||||
Content string `orm:"column(content);type(text)" json:"content"`
|
||||
SenderID uint64 `orm:"column(sender_id);default(0)" json:"sender_id"`
|
||||
SenderType string `orm:"column(sender_type);size(32);default('system')" json:"sender_type"` // system, platform, tenant
|
||||
ReceiverID uint64 `orm:"column(receiver_id)" json:"receiver_id"`
|
||||
ReceiverType string `orm:"column(receiver_type);size(32)" json:"receiver_type"` // platform, tenant
|
||||
IsRead int8 `orm:"column(is_read);default(0)" json:"is_read"` // 0-未读, 1-已读
|
||||
ReadTime *time.Time `orm:"column(read_time);type(datetime);null" json:"read_time"`
|
||||
CreateTime *time.Time `orm:"column(create_time);type(datetime);null" json:"create_time"`
|
||||
DeleteTime *time.Time `orm:"column(delete_time);type(datetime);null" json:"delete_time"`
|
||||
BatchID string `orm:"column(batch_id);size(64);default('')" json:"batch_id"`
|
||||
// TargetType 接收目标类型:
|
||||
// all 全平台 / platform 平台端 / tenant_all 租户端(所有租户) /
|
||||
// tenant_role 租户角色 / tenant 租户 / tenant_user 租户用户
|
||||
// 兼容旧数据:role 平台角色
|
||||
TargetType string `orm:"column(target_type);size(32);default('')" json:"target_type"`
|
||||
// TargetIDs 多选接收目标ID列表(JSON 数组字符串),按 TargetType 解释:
|
||||
// tenant_role=角色ID列表, tenant=租户ID列表, tenant_user=用户ID列表
|
||||
TargetIDs *string `orm:"column(target_ids);type(text);null" json:"target_ids"`
|
||||
TargetRoleID uint64 `orm:"column(target_role_id);default(0)" json:"target_role_id"` // 兼容旧数据:平台角色ID
|
||||
TargetTenantID uint64 `orm:"column(target_tenant_id);default(0)" json:"target_tenant_id"` // 兼容旧数据:特定租户ID
|
||||
}
|
||||
|
||||
func (m *SystemReminderList) TableName() string {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -32,34 +31,4 @@ func (m *SystemTenantUser) TableName() string {
|
||||
return "yz_system_tenant_user"
|
||||
}
|
||||
|
||||
var tenantUserGroupColOnce sync.Once
|
||||
|
||||
// EnsureTenantUserGroupColumn 为租户用户表补齐 group_id(角色)列。
|
||||
// 历史库可能早于"用户-角色"特性建表,字段已存在时 MySQL 报 duplicate column,统一忽略。
|
||||
func EnsureTenantUserGroupColumn() {
|
||||
if Orm == nil {
|
||||
return
|
||||
}
|
||||
tenantUserGroupColOnce.Do(func() {
|
||||
_, _ = Orm.Raw(`
|
||||
ALTER TABLE yz_system_tenant_user
|
||||
ADD COLUMN group_id bigint unsigned NOT NULL DEFAULT 0 COMMENT '角色ID 关联 yz_system_admin_role(cid=2)'
|
||||
`).Exec()
|
||||
})
|
||||
}
|
||||
|
||||
var tenantUserOrgColOnce sync.Once
|
||||
|
||||
// EnsureTenantUserOrgColumn 为租户用户表补齐 org_id(所属部门)列。
|
||||
// 与 group_id 同样的幂等策略:字段已存在时 MySQL 报 duplicate column,统一忽略。
|
||||
func EnsureTenantUserOrgColumn() {
|
||||
if Orm == nil {
|
||||
return
|
||||
}
|
||||
tenantUserOrgColOnce.Do(func() {
|
||||
_, _ = Orm.Raw(`
|
||||
ALTER TABLE yz_system_tenant_user
|
||||
ADD COLUMN org_id bigint unsigned NOT NULL DEFAULT 0 COMMENT '所属组织ID 关联 yz_backend_organization(id),0-未分配'
|
||||
`).Exec()
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user