package app import ( "context" "crypto/rand" "fmt" "strings" "git.5iot.com/iotlcm-public/iotlcm-app/internal/consts" "git.5iot.com/iotlcm-public/iotlcm-app/internal/dao" "git.5iot.com/iotlcm-public/iotlcm-app/internal/library/appidentity" "git.5iot.com/iotlcm-public/iotlcm-app/internal/library/batchimport" "git.5iot.com/iotlcm-public/iotlcm-app/internal/library/contexts" "git.5iot.com/iotlcm-public/iotlcm-app/internal/model" "git.5iot.com/iotlcm-public/iotlcm-app/internal/model/entity" "git.5iot.com/iotlcm-public/iotlcm-app/utility" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/os/gtime" ) const appCardBatchNoAlphabet = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789" func (s *sAppCard) isTerminalUser(ctx context.Context, user *entity.SysUser) bool { return appIsTerminalUser(ctx, user) } func (s *sAppCard) ownerType(ctx context.Context, user *entity.SysUser) string { if s.isTerminalUser(ctx, user) { return appCardOwnerTerminal } return appCardOwnerAgent } func (s *sAppCard) canOperateCard(ctx context.Context, user *entity.SysUser, card *appCardQueryRow) bool { if user == nil || card == nil { return false } if s.isTerminalUser(ctx, user) { return card.TerminalUserId == user.UserId } return user.DeptId > 0 } func (s *sAppCard) ensureOperateCard(ctx context.Context, user *entity.SysUser, card *appCardQueryRow, actionName string) error { if s.canOperateCard(ctx, user, card) { return nil } cardLabel := "" if card != nil { cardLabel = appFirstNotEmpty(card.Iccid, card.Msisdn, card.Imsi, card.Imei) } actionName = strings.TrimSpace(actionName) if actionName == "" { actionName = "办理业务" } if s.isTerminalUser(ctx, user) { return gerror.Newf("卡%s不在当前终端用户可操作范围内", cardLabel) } return gerror.Newf("卡%s不在当前企业可操作范围内", cardLabel) } func (s *sAppCard) canOperatePackageRow(ctx context.Context, user *entity.SysUser, row *appPackageQueryRow) bool { if user == nil || row == nil { return false } if s.isTerminalUser(ctx, user) { return true } return user.DeptId > 0 } func appIsTerminalUser(ctx context.Context, user *entity.SysUser) bool { isTerminal, err := appidentity.IsTerminalUser(ctx, user) return err == nil && isTerminal } // appEnterpriseDeptAndSubIds 返回企业账号可查看的机构范围:当前机构及所有下级机构。 // 这里按 sys_dept.parent_id 逐层递归,不依赖 ancestors 字符串,避免祖级字段不规范导致漏数据。 func appEnterpriseDeptAndSubIds(ctx context.Context, tenantId string, rootDeptId int64) ([]int64, error) { if rootDeptId <= 0 { return []int64{}, nil } tenantId = strings.TrimSpace(tenantId) if tenantId == "" { tenantId = consts.DefaultSystemTenantCode } seen := map[int64]struct{}{ rootDeptId: {}, } result := []int64{rootDeptId} level := []int64{rootDeptId} columns := dao.SysDept.Columns() for len(level) > 0 { var rows []*entity.SysDept err := dao.SysDept.Ctx(ctx). Fields(columns.DeptId). Where(columns.TenantId, tenantId). WhereIn(columns.ParentId, level). WhereNull(columns.DeletedAt). Scan(&rows) if err != nil { return nil, gerror.Wrap(err, "查询企业下级机构失败") } nextLevel := make([]int64, 0, len(rows)) for _, row := range rows { if row == nil || row.DeptId <= 0 { continue } if _, ok := seen[row.DeptId]; ok { continue } seen[row.DeptId] = struct{}{} result = append(result, row.DeptId) nextLevel = append(nextLevel, row.DeptId) } level = nextLevel } return result, nil } // appEnterpriseDisplayDept 按当前企业视角返回卡片归属展示机构。 // 真实卡归属可能在下下级,前端只允许看到本级或直属下级,避免泄露更深层级组织信息。 func appEnterpriseDisplayDept(ctx context.Context, tenantId string, rootDeptId int64, actualDeptId int64) (*entity.SysDept, error) { if rootDeptId <= 0 || actualDeptId <= 0 { return nil, nil } if actualDeptId == rootDeptId { return appLoadDeptForDisplay(ctx, tenantId, rootDeptId) } currentDeptId := actualDeptId seen := make(map[int64]struct{}) for currentDeptId > 0 { if _, ok := seen[currentDeptId]; ok { return nil, nil } seen[currentDeptId] = struct{}{} dept, err := appLoadDeptForDisplay(ctx, tenantId, currentDeptId) if err != nil { return nil, err } if dept == nil || dept.DeptId == 0 { return nil, nil } if dept.ParentId == rootDeptId { return dept, nil } if dept.ParentId <= 0 || dept.ParentId == dept.DeptId { return nil, nil } currentDeptId = dept.ParentId } return nil, nil } func appLoadDeptForDisplay(ctx context.Context, tenantId string, deptId int64) (*entity.SysDept, error) { tenantId = strings.TrimSpace(tenantId) if tenantId == "" { tenantId = consts.DefaultSystemTenantCode } var dept *entity.SysDept if err := dao.SysDept.Ctx(ctx). Where(dao.SysDept.Columns().TenantId, tenantId). Where(dao.SysDept.Columns().DeptId, deptId). WhereNull(dao.SysDept.Columns().DeletedAt). Limit(1). Scan(&dept); err != nil { return nil, gerror.Wrap(err, "查询机构展示信息失败") } return dept, nil } func appCurrentUser(ctx context.Context) (*entity.SysUser, error) { userId := contexts.GetUserId(ctx) if userId == 0 { return nil, gerror.New("登录已过期") } var user *entity.SysUser err := dao.SysUser.Ctx(ctx). Where(dao.SysUser.Columns().UserId, userId). Scan(&user) if err != nil { return nil, err } if user == nil { return nil, gerror.New("用户不存在") } return user, nil } func appContextWithUser(ctx context.Context, user *entity.SysUser) context.Context { if user == nil || user.UserId <= 0 { return ctx } if current := contexts.GetUser(ctx); current != nil && current.ID > 0 && strings.TrimSpace(current.TenantId) != "" { return ctx } return contexts.Set(ctx, &model.Context{ User: &model.Identity{ BaseClaims: model.BaseClaims{ ID: user.UserId, Username: user.UserName, NickName: user.NickName, TenantId: user.TenantId, DeptId: user.DeptId, }, }, }) } func appCardValueCondition() string { cardTable := dao.TSimCardInfo.Table() return "(" + cardTable + "." + dao.TSimCardInfo.Columns().Iccid + " = ? OR " + cardTable + "." + dao.TSimCardInfo.Columns().Msisdn + " = ? OR " + cardTable + "." + dao.TSimCardInfo.Columns().Imsi + " = ? OR " + cardTable + "." + dao.TSimCardInfo.Columns().Imei + " = ?)" } func appCardValueArgs(cardValue string) []interface{} { value := strings.TrimSpace(cardValue) return []interface{}{value, value, value, value} } func appNextBatchNo(ctx context.Context, prefix string) (string, error) { return batchimport.NextBatchNoContext(ctx, prefix) } func appCardBatchNo(prefix string, userId int64, now *gtime.Time) string { if now == nil { now = gtime.Now() } prefix = strings.TrimSpace(strings.ToUpper(prefix)) random := make([]byte, 8) randomBytes := make([]byte, len(random)) if _, err := rand.Read(randomBytes); err != nil { fallback := strings.ToUpper(utility.RandomString(len(random))) for i := range random { random[i] = appCardBatchNoAlphabet[int(fallback[i])%len(appCardBatchNoAlphabet)] } return fmt.Sprintf("%s%s%s", prefix, now.Format("YmdHis"), string(random)) } for i, b := range randomBytes { random[i] = appCardBatchNoAlphabet[int(b)%len(appCardBatchNoAlphabet)] } return fmt.Sprintf("%s%s%s", prefix, now.Format("YmdHis"), string(random)) } func appOrderNo(prefix string, userId int64, now *gtime.Time) string { return appCardBatchNo(prefix, userId, now) }