数据迁移(北京 / 武汉两个 SQLite 物料库 → wmms) - 北京进默认组织、武汉单独建组织:两套分类编码完全相同但含义不同(北京 A01=标准件, 武汉 A01=机加工件),共存会撞 material_categories 的组织内编码唯一约束 - 199 条物料、37 个企业、45 条变更日志,以及各分类的 code_rules.next_sequence (对齐到已导入的最大序号 +1,否则用户新增物料会撞上已有编码) - 迁移段并入 scripts/init.sql 末尾,空库初始化即得完整数据 公司默认数据 - 新建公司时种入 32 个物料单位 + 默认仓库 + 主库位,与建公司同事务, 种一半失败会整体回滚(否则留下一个没单位、又占着公司编码的组织) - 单位在后端按公司隔离(MaterialRepo.IsActiveUnit 要求物料引用的单位属于本公司), 此前新建的公司连单位下拉都是空的,物料的 base_unit_id 也无处可指 - init.sql 的单位段补齐「块」「Pcs」,与代码里的清单保持逐条一致 默认组织更名:默认公司组织 → 北京
192 lines
5.6 KiB
Go
192 lines
5.6 KiB
Go
package repository
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/wmms/wmms/internal/model"
|
|
"github.com/wmms/wmms/pkg/database"
|
|
)
|
|
|
|
type OrganizationRepository struct{}
|
|
|
|
var ErrProtectedRole = errors.New("super administrator role cannot be changed")
|
|
|
|
func NewOrganizationRepository() *OrganizationRepository { return &OrganizationRepository{} }
|
|
|
|
func (r *OrganizationRepository) List() ([]model.Organization, error) {
|
|
rows, err := database.DB.Query(`SELECT id, code, name, status, default_timezone, created_at, updated_at
|
|
FROM organizations ORDER BY id`)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
organizations := make([]model.Organization, 0)
|
|
for rows.Next() {
|
|
var item model.Organization
|
|
if err := rows.Scan(&item.ID, &item.Code, &item.Name, &item.Status, &item.DefaultTimezone, &item.CreatedAt, &item.UpdatedAt); err != nil {
|
|
return nil, err
|
|
}
|
|
organizations = append(organizations, item)
|
|
}
|
|
return organizations, rows.Err()
|
|
}
|
|
|
|
// Create 建公司并同时种上默认主数据(物料单位、默认仓库与主库位)。两者放在一个
|
|
// 事务里:只建成公司却没种上单位,新公司连物料单位下拉都是空的,而用户重试还会
|
|
// 撞上公司编码的唯一键。
|
|
func (r *OrganizationRepository) Create(item *model.Organization) error {
|
|
tx, err := database.DB.Begin()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer func() { _ = tx.Rollback() }()
|
|
|
|
result, err := tx.Exec(`INSERT INTO organizations (code, name, status, default_timezone) VALUES (?, ?, ?, ?)`, item.Code, item.Name, item.Status, item.DefaultTimezone)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
item.ID, err = result.LastInsertId()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := seedOrganizationDefaultsTx(tx, item.ID); err != nil {
|
|
return err
|
|
}
|
|
return tx.Commit()
|
|
}
|
|
|
|
func (r *OrganizationRepository) Update(item *model.Organization) error {
|
|
return execExpectingRow(`UPDATE organizations SET name = ?, status = ?, default_timezone = ? WHERE id = ?`, item.Name, item.Status, item.DefaultTimezone, item.ID)
|
|
}
|
|
|
|
func (r *OrganizationRepository) ListRoles() ([]model.Role, error) {
|
|
rows, err := database.DB.Query(`SELECT id, code, name, enabled, created_at, updated_at FROM roles ORDER BY id`)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
roles := make([]model.Role, 0)
|
|
roleIndexes := make(map[int64]int)
|
|
for rows.Next() {
|
|
var item model.Role
|
|
if err := rows.Scan(&item.ID, &item.Code, &item.Name, &item.Enabled, &item.CreatedAt, &item.UpdatedAt); err != nil {
|
|
return nil, err
|
|
}
|
|
item.Permissions = []model.Permission{}
|
|
roleIndexes[item.ID] = len(roles)
|
|
roles = append(roles, item)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
permissionRows, err := database.DB.Query(`SELECT rp.role_id, p.id, p.code, p.name
|
|
FROM role_permissions rp JOIN permissions p ON p.id = rp.permission_id
|
|
ORDER BY rp.role_id, p.code`)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer permissionRows.Close()
|
|
for permissionRows.Next() {
|
|
var roleID int64
|
|
var permission model.Permission
|
|
if err := permissionRows.Scan(&roleID, &permission.ID, &permission.Code, &permission.Name); err != nil {
|
|
return nil, err
|
|
}
|
|
if index, ok := roleIndexes[roleID]; ok {
|
|
roles[index].Permissions = append(roles[index].Permissions, permission)
|
|
}
|
|
}
|
|
return roles, permissionRows.Err()
|
|
}
|
|
|
|
func (r *OrganizationRepository) ListPermissions() ([]model.Permission, error) {
|
|
rows, err := database.DB.Query(`SELECT id, code, name FROM permissions ORDER BY code`)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
permissions := make([]model.Permission, 0)
|
|
for rows.Next() {
|
|
var permission model.Permission
|
|
if err := rows.Scan(&permission.ID, &permission.Code, &permission.Name); err != nil {
|
|
return nil, err
|
|
}
|
|
permissions = append(permissions, permission)
|
|
}
|
|
return permissions, rows.Err()
|
|
}
|
|
|
|
func (r *OrganizationRepository) UpdateRole(roleID int64, name string, enabled bool, permissionCodes []string) (err error) {
|
|
tx, err := database.DB.Begin()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer func() {
|
|
if err != nil {
|
|
_ = tx.Rollback()
|
|
}
|
|
}()
|
|
|
|
var roleCode string
|
|
if err = tx.QueryRow(`SELECT code FROM roles WHERE id = ? FOR UPDATE`, roleID).Scan(&roleCode); err != nil {
|
|
return err
|
|
}
|
|
if roleCode == "super_admin" {
|
|
return ErrProtectedRole
|
|
}
|
|
if _, err = tx.Exec(`UPDATE roles SET name = ?, enabled = ? WHERE id = ?`, name, enabled, roleID); err != nil {
|
|
return err
|
|
}
|
|
if _, err = tx.Exec(`DELETE FROM role_permissions WHERE role_id = ?`, roleID); err != nil {
|
|
return err
|
|
}
|
|
|
|
permissionCodes = uniqueStrings(permissionCodes)
|
|
if len(permissionCodes) > 0 {
|
|
placeholders := strings.TrimSuffix(strings.Repeat("?, ", len(permissionCodes)), ", ")
|
|
query := fmt.Sprintf(`INSERT INTO role_permissions (role_id, permission_id)
|
|
SELECT ?, id FROM permissions WHERE code IN (%s)`, placeholders)
|
|
args := make([]interface{}, 0, len(permissionCodes)+1)
|
|
args = append(args, roleID)
|
|
for _, code := range permissionCodes {
|
|
args = append(args, code)
|
|
}
|
|
result, execErr := tx.Exec(query, args...)
|
|
if execErr != nil {
|
|
err = execErr
|
|
return err
|
|
}
|
|
var affected int64
|
|
affected, err = result.RowsAffected()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if affected != int64(len(permissionCodes)) {
|
|
return fmt.Errorf("one or more permission codes do not exist")
|
|
}
|
|
}
|
|
return tx.Commit()
|
|
}
|
|
|
|
func uniqueStrings(values []string) []string {
|
|
seen := make(map[string]struct{}, len(values))
|
|
result := make([]string, 0, len(values))
|
|
for _, value := range values {
|
|
value = strings.TrimSpace(value)
|
|
if value == "" {
|
|
continue
|
|
}
|
|
if _, exists := seen[value]; exists {
|
|
continue
|
|
}
|
|
seen[value] = struct{}{}
|
|
result = append(result, value)
|
|
}
|
|
return result
|
|
}
|