Files
yunzerwebsiteallinone/go/models/init.go
T
2026-09-15 00:08:36 +08:00

418 lines
19 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package models
import (
"fmt"
"github.com/beego/beego/v2/client/orm"
beego "github.com/beego/beego/v2/server/web"
_ "github.com/go-sql-driver/mysql"
)
// Orm 全局 ORM 对象,供业务层和控制器使用
var Orm orm.Ormer
// Init 初始化模型层资源(数据库连接、模型注册等)。
func Init(_ string) {
// 从配置读取数据库连接信息
user, _ := beego.AppConfig.String("mysqluser")
pass, _ := beego.AppConfig.String("mysqlpass")
urls, _ := beego.AppConfig.String("mysqlurls")
dbname, _ := beego.AppConfig.String("mysqldb")
if user == "" || urls == "" || dbname == "" {
panic("数据库配置(mysqluser/mysqlurls/mysqldb) 未正确设置")
}
// 组装 DSN:user:pass@tcp(host:port)/dbname?charset=utf8mb4&parseTime=True&loc=Local
dsn := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", user, pass, urls, dbname)
// 注册默认数据库
if err := orm.RegisterDataBase("default", "mysql", dsn); err != nil {
panic("注册数据库失败: " + err.Error())
}
// 注册模型
orm.RegisterModel(
new(SystemTenant),
new(SystemTenantUser),
new(BackendOrganization),
new(BackendEmployee),
new(BackendPosition),
new(BackendErpContact),
new(BackendApprovalFlow),
new(BackendApprovalRecord),
new(BackendReimbursement),
new(BackendReimbursementItem),
new(BackendReimbursementExpenseType),
new(SystemMenu),
new(AdminUser),
new(AdminRole),
new(SystemFile),
new(SystemFilesCategory),
new(SystemSMSTask),
new(SystemOperationLog),
new(SystemLoginLog),
new(SystemDomainPool),
new(SystemTenantDomain),
new(SystemModules),
new(StorageConfig),
new(TenantSiteSetting),
new(PlatformSiteSetting),
new(ComplaintCategory),
new(PlatformComplaint),
new(SystemSoftwareUpgrade),
new(PlatformCursorEquipment),
new(PlatformCursorActivationCode),
new(PlatformCursorEquipmentIpLog),
new(PlatformAccountPoolKiro),
new(PlatformAccountPoolWindsurf),
new(PlatformAccountPoolCursor),
new(PlatformAccountPoolCodex),
new(PlatformNotebook),
new(PlatformAgentApi),
new(PlatformPasswordStore),
new(BackendPasswordStore),
new(BackendUserNotifyConfig),
new(TenantCrmCustomer),
new(TenantCrmSupplier),
new(TenantCrmClue),
new(TenantCrmBusiness),
new(TenantCrmProject),
new(TenantCrmProduct),
new(TenantCrmProductCategory),
new(TenantCrmUnit),
new(TenantCrmFollow),
new(TenantCrmAttach),
new(TenantCrmEntityContact),
new(TenantCrmOperateLog),
new(TenantCrmContract),
new(TenantCrmPayback),
new(TenantCrmPaybackRecord),
new(ErpAccountSet),
new(ErpNormalSetting),
new(ErpCompanyContact),
new(BackendAiProvider),
new(BackendAiChatSession),
new(BackendAiChatMessage),
new(BackendAiChatPreset),
new(BackendMcpTool),
new(BackendMcpServer),
new(CmsArticleCategory),
new(CmsArticle),
new(CmsProductCategory),
new(CmsProduct),
new(CmsSolutionCategory),
new(CmsSolution),
new(CmsFrontendTemplate),
new(CmsFrontendBanner),
new(CmsFrontendFriendlink),
new(CmsFrontendOnepage),
new(BackendMenuFront),
new(SystemReminderList),
new(SystemNormalSetting),
new(PlatformNormalSetting),
new(BackendNormalSetting),
new(PlatformSchedule),
new(PlatformScheduleReminder),
new(PlatformScheduleReminderSendLog),
new(BackendNotebook),
new(BackendSchedule),
new(BackendScheduleReminder),
new(BackendScheduleReminderSendLog),
new(OaSchedule),
new(OaNotice),
new(OaDocCategory),
new(OaDoc),
new(OaDocLink),
new(OaDocShare),
new(BackendOaCompensationScheme),
new(BackendOaPayroll),
new(BackendOaPayrollItem),
new(BackendEmployeeFile),
new(BackendEmployeeFileRecord),
)
// 创建全局 Ormer
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
}
}
}
}