This commit is contained in:
@@ -57,6 +57,7 @@ func createAdminAuthMiddleware(tokenManager *pkgauth.TokenManager, shopStore pkg
|
|||||||
return &pkgmiddleware.UserContextInfo{
|
return &pkgmiddleware.UserContextInfo{
|
||||||
UserID: tokenInfo.UserID,
|
UserID: tokenInfo.UserID,
|
||||||
UserType: tokenInfo.UserType,
|
UserType: tokenInfo.UserType,
|
||||||
|
Username: tokenInfo.Username,
|
||||||
ShopID: tokenInfo.ShopID,
|
ShopID: tokenInfo.ShopID,
|
||||||
EnterpriseID: tokenInfo.EnterpriseID,
|
EnterpriseID: tokenInfo.EnterpriseID,
|
||||||
}, nil
|
}, nil
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ import (
|
|||||||
"github.com/break/junhong_cmp_fiber/internal/polling"
|
"github.com/break/junhong_cmp_fiber/internal/polling"
|
||||||
accountSvc "github.com/break/junhong_cmp_fiber/internal/service/account"
|
accountSvc "github.com/break/junhong_cmp_fiber/internal/service/account"
|
||||||
accountAuditSvc "github.com/break/junhong_cmp_fiber/internal/service/account_audit"
|
accountAuditSvc "github.com/break/junhong_cmp_fiber/internal/service/account_audit"
|
||||||
assetAuditSvc "github.com/break/junhong_cmp_fiber/internal/service/asset_audit"
|
|
||||||
assetAllocationRecordSvc "github.com/break/junhong_cmp_fiber/internal/service/asset_allocation_record"
|
assetAllocationRecordSvc "github.com/break/junhong_cmp_fiber/internal/service/asset_allocation_record"
|
||||||
|
assetAuditSvc "github.com/break/junhong_cmp_fiber/internal/service/asset_audit"
|
||||||
authSvc "github.com/break/junhong_cmp_fiber/internal/service/auth"
|
authSvc "github.com/break/junhong_cmp_fiber/internal/service/auth"
|
||||||
carrierSvc "github.com/break/junhong_cmp_fiber/internal/service/carrier"
|
carrierSvc "github.com/break/junhong_cmp_fiber/internal/service/carrier"
|
||||||
clientAuthSvc "github.com/break/junhong_cmp_fiber/internal/service/client_auth"
|
clientAuthSvc "github.com/break/junhong_cmp_fiber/internal/service/client_auth"
|
||||||
@@ -112,7 +112,7 @@ type services struct {
|
|||||||
func initServices(s *stores, deps *Dependencies) *services {
|
func initServices(s *stores, deps *Dependencies) *services {
|
||||||
purchaseValidation := purchaseValidationSvc.New(deps.DB, s.IotCard, s.Device, s.Package, s.ShopPackageAllocation)
|
purchaseValidation := purchaseValidationSvc.New(deps.DB, s.IotCard, s.Device, s.Package, s.ShopPackageAllocation)
|
||||||
accountAudit := accountAuditSvc.NewService(s.AccountOperationLog)
|
accountAudit := accountAuditSvc.NewService(s.AccountOperationLog)
|
||||||
assetAudit := assetAuditSvc.NewService(s.AssetOperationLog)
|
assetAudit := assetAuditSvc.NewService(s.AssetOperationLog, deps.DB)
|
||||||
account := accountSvc.New(s.Account, s.Role, s.AccountRole, s.ShopRole, s.Shop, s.Enterprise, accountAudit)
|
account := accountSvc.New(s.Account, s.Role, s.AccountRole, s.ShopRole, s.Shop, s.Enterprise, accountAudit)
|
||||||
|
|
||||||
// 创建 IotCard service 并设置回调
|
// 创建 IotCard service 并设置回调
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ type workerServices struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func initWorkerServices(stores *queue.WorkerStores, deps *WorkerDependencies) *queue.WorkerServices {
|
func initWorkerServices(stores *queue.WorkerStores, deps *WorkerDependencies) *queue.WorkerServices {
|
||||||
assetAudit := assetAuditSvc.NewService(stores.AssetOperationLog)
|
assetAudit := assetAuditSvc.NewService(stores.AssetOperationLog, deps.DB)
|
||||||
commissionStatsService := commission_stats.New(stores.ShopSeriesCommissionStats)
|
commissionStatsService := commission_stats.New(stores.ShopSeriesCommissionStats)
|
||||||
|
|
||||||
commissionCalculationService := commission_calculation.New(
|
commissionCalculationService := commission_calculation.New(
|
||||||
|
|||||||
@@ -16,9 +16,10 @@ type AssetOperationLogItem struct {
|
|||||||
ID uint `json:"id" description:"日志ID"`
|
ID uint `json:"id" description:"日志ID"`
|
||||||
CreatedAt time.Time `json:"created_at" description:"操作时间"`
|
CreatedAt time.Time `json:"created_at" description:"操作时间"`
|
||||||
|
|
||||||
OperatorID *uint `json:"operator_id,omitempty" description:"操作人ID(系统触发为空)"`
|
OperatorID *uint `json:"operator_id,omitempty" description:"操作人ID(系统触发为空)"`
|
||||||
OperatorType string `json:"operator_type" description:"操作人类型(system/admin_user/agent_user/enterprise_user/personal_customer)"`
|
OperatorType string `json:"operator_type" description:"操作人类型名称 (系统:系统, 平台用户:平台用户, 代理账号:代理账号, 企业账号:企业账号, 个人客户:个人客户)"`
|
||||||
OperatorName string `json:"operator_name" description:"操作人名称快照"`
|
OperatorTypeCode string `json:"operator_type_code" description:"操作人类型编码(system/admin_user/agent_user/enterprise_user/personal_customer)"`
|
||||||
|
OperatorName string `json:"operator_name" description:"操作人名称(优先返回可读名称)"`
|
||||||
|
|
||||||
AssetType string `json:"asset_type" description:"资产类型(iot_card/device)"`
|
AssetType string `json:"asset_type" description:"资产类型(iot_card/device)"`
|
||||||
AssetID uint `json:"asset_id" description:"资产ID"`
|
AssetID uint `json:"asset_id" description:"资产ID"`
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package asset_audit
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
stderrors "errors"
|
stderrors "errors"
|
||||||
"fmt"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -100,6 +99,7 @@ func BuildLog(ctx context.Context, p BuildLogParams) *model.AssetOperationLog {
|
|||||||
func OperatorFromContext(ctx context.Context) OperatorInfo {
|
func OperatorFromContext(ctx context.Context) OperatorInfo {
|
||||||
uid := middleware.GetUserIDFromContext(ctx)
|
uid := middleware.GetUserIDFromContext(ctx)
|
||||||
ut := middleware.GetUserTypeFromContext(ctx)
|
ut := middleware.GetUserTypeFromContext(ctx)
|
||||||
|
username := strings.TrimSpace(middleware.GetUsernameFromContext(ctx))
|
||||||
|
|
||||||
if uid == 0 {
|
if uid == 0 {
|
||||||
return SystemOperator("系统任务")
|
return SystemOperator("系统任务")
|
||||||
@@ -109,19 +109,19 @@ func OperatorFromContext(ctx context.Context) OperatorInfo {
|
|||||||
switch ut {
|
switch ut {
|
||||||
case constants.UserTypeSuperAdmin, constants.UserTypePlatform:
|
case constants.UserTypeSuperAdmin, constants.UserTypePlatform:
|
||||||
op.Type = constants.AssetAuditOperatorTypeAdmin
|
op.Type = constants.AssetAuditOperatorTypeAdmin
|
||||||
op.Name = fmt.Sprintf("后台用户#%d", uid)
|
op.Name = fallbackOperatorName(username, "平台用户")
|
||||||
case constants.UserTypeAgent:
|
case constants.UserTypeAgent:
|
||||||
op.Type = constants.AssetAuditOperatorTypeAgent
|
op.Type = constants.AssetAuditOperatorTypeAgent
|
||||||
op.Name = fmt.Sprintf("代理用户#%d", uid)
|
op.Name = fallbackOperatorName(username, "代理账号")
|
||||||
case constants.UserTypeEnterprise:
|
case constants.UserTypeEnterprise:
|
||||||
op.Type = constants.AssetAuditOperatorTypeEnterprise
|
op.Type = constants.AssetAuditOperatorTypeEnterprise
|
||||||
op.Name = fmt.Sprintf("企业用户#%d", uid)
|
op.Name = fallbackOperatorName(username, "企业账号")
|
||||||
case constants.UserTypePersonalCustomer:
|
case constants.UserTypePersonalCustomer:
|
||||||
op.Type = constants.AssetAuditOperatorTypePersonal
|
op.Type = constants.AssetAuditOperatorTypePersonal
|
||||||
op.Name = fmt.Sprintf("个人客户#%d", uid)
|
op.Name = fallbackOperatorName(username, "个人客户")
|
||||||
default:
|
default:
|
||||||
op.Type = constants.AssetAuditOperatorTypeAdmin
|
op.Type = constants.AssetAuditOperatorTypeAdmin
|
||||||
op.Name = fmt.Sprintf("用户#%d", uid)
|
op.Name = fallbackOperatorName(username, "平台用户")
|
||||||
}
|
}
|
||||||
return op
|
return op
|
||||||
}
|
}
|
||||||
@@ -267,3 +267,10 @@ func strPtr(v string) *string {
|
|||||||
func uintPtr(v uint) *uint {
|
func uintPtr(v uint) *uint {
|
||||||
return &v
|
return &v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func fallbackOperatorName(name string, fallback string) string {
|
||||||
|
if strings.TrimSpace(name) != "" {
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
return fallback
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,12 +3,15 @@ package asset_audit
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/break/junhong_cmp_fiber/internal/model"
|
"github.com/break/junhong_cmp_fiber/internal/model"
|
||||||
"github.com/break/junhong_cmp_fiber/internal/model/dto"
|
"github.com/break/junhong_cmp_fiber/internal/model/dto"
|
||||||
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
||||||
"github.com/break/junhong_cmp_fiber/pkg/logger"
|
"github.com/break/junhong_cmp_fiber/pkg/logger"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AssetOperationLogStore 资产操作日志存储接口。
|
// AssetOperationLogStore 资产操作日志存储接口。
|
||||||
@@ -33,6 +36,7 @@ type OperationLogger interface {
|
|||||||
// Service 资产审计服务。
|
// Service 资产审计服务。
|
||||||
type Service struct {
|
type Service struct {
|
||||||
store AssetOperationLogStore
|
store AssetOperationLogStore
|
||||||
|
db *gorm.DB
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListByAssetParams 按资产查询日志参数。
|
// ListByAssetParams 按资产查询日志参数。
|
||||||
@@ -46,8 +50,11 @@ type ListByAssetParams struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewService 创建资产审计服务实例。
|
// NewService 创建资产审计服务实例。
|
||||||
func NewService(store AssetOperationLogStore) *Service {
|
func NewService(store AssetOperationLogStore, db *gorm.DB) *Service {
|
||||||
return &Service{store: store}
|
return &Service{
|
||||||
|
store: store,
|
||||||
|
db: db,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// LogOperation 记录资产操作日志(异步写入,不阻塞主流程)。
|
// LogOperation 记录资产操作日志(异步写入,不阻塞主流程)。
|
||||||
@@ -104,9 +111,10 @@ func (s *Service) ListByAsset(ctx context.Context, params ListByAssetParams) (*d
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
operatorViews := s.loadOperatorViews(ctx, logs)
|
||||||
items := make([]*dto.AssetOperationLogItem, 0, len(logs))
|
items := make([]*dto.AssetOperationLogItem, 0, len(logs))
|
||||||
for _, item := range logs {
|
for _, item := range logs {
|
||||||
items = append(items, toAssetOperationLogItem(item))
|
items = append(items, toAssetOperationLogItem(item, operatorViews[item.ID]))
|
||||||
}
|
}
|
||||||
|
|
||||||
return &dto.AssetOperationLogListResponse{
|
return &dto.AssetOperationLogListResponse{
|
||||||
@@ -117,21 +125,24 @@ func (s *Service) ListByAsset(ctx context.Context, params ListByAssetParams) (*d
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func toAssetOperationLogItem(log *model.AssetOperationLog) *dto.AssetOperationLogItem {
|
func toAssetOperationLogItem(log *model.AssetOperationLog, operatorView operatorView) *dto.AssetOperationLogItem {
|
||||||
if log == nil {
|
if log == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
operationBefore, operationAfter := normalizeOperationContent(
|
operationBefore, operationAfter := normalizeOperationContent(
|
||||||
log.OperationType,
|
log.OperationType,
|
||||||
map[string]any(log.BeforeData),
|
map[string]any(log.BeforeData),
|
||||||
map[string]any(log.AfterData),
|
map[string]any(log.AfterData),
|
||||||
)
|
)
|
||||||
|
|
||||||
return &dto.AssetOperationLogItem{
|
return &dto.AssetOperationLogItem{
|
||||||
ID: log.ID,
|
ID: log.ID,
|
||||||
CreatedAt: log.CreatedAt,
|
CreatedAt: log.CreatedAt,
|
||||||
OperatorID: log.OperatorID,
|
OperatorID: log.OperatorID,
|
||||||
OperatorType: log.OperatorType,
|
OperatorType: operatorView.TypeName,
|
||||||
OperatorName: log.OperatorName,
|
OperatorTypeCode: log.OperatorType,
|
||||||
|
OperatorName: operatorView.Name,
|
||||||
AssetType: log.AssetType,
|
AssetType: log.AssetType,
|
||||||
AssetID: log.AssetID,
|
AssetID: log.AssetID,
|
||||||
AssetIdentifier: log.AssetIdentifier,
|
AssetIdentifier: log.AssetIdentifier,
|
||||||
@@ -155,3 +166,269 @@ func toAssetOperationLogItem(log *model.AssetOperationLog) *dto.AssetOperationLo
|
|||||||
FailCount: log.FailCount,
|
FailCount: log.FailCount,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type operatorView struct {
|
||||||
|
TypeName string
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) loadOperatorViews(ctx context.Context, logs []*model.AssetOperationLog) map[uint]operatorView {
|
||||||
|
result := make(map[uint]operatorView, len(logs))
|
||||||
|
if len(logs) == 0 {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
accountIDs := make([]uint, 0)
|
||||||
|
customerIDs := make([]uint, 0)
|
||||||
|
accountIDSet := make(map[uint]struct{})
|
||||||
|
customerIDSet := make(map[uint]struct{})
|
||||||
|
|
||||||
|
for _, log := range logs {
|
||||||
|
if log == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
typeName := operatorTypeDisplayName(log.OperatorType)
|
||||||
|
result[log.ID] = operatorView{
|
||||||
|
TypeName: typeName,
|
||||||
|
Name: fallbackResolvedOperatorName(log.OperatorName, typeName, log.OperatorID),
|
||||||
|
}
|
||||||
|
|
||||||
|
if log.OperatorID == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
switch log.OperatorType {
|
||||||
|
case constants.AssetAuditOperatorTypeAdmin, constants.AssetAuditOperatorTypeAgent, constants.AssetAuditOperatorTypeEnterprise:
|
||||||
|
if _, exists := accountIDSet[*log.OperatorID]; !exists {
|
||||||
|
accountIDSet[*log.OperatorID] = struct{}{}
|
||||||
|
accountIDs = append(accountIDs, *log.OperatorID)
|
||||||
|
}
|
||||||
|
case constants.AssetAuditOperatorTypePersonal:
|
||||||
|
if _, exists := customerIDSet[*log.OperatorID]; !exists {
|
||||||
|
customerIDSet[*log.OperatorID] = struct{}{}
|
||||||
|
customerIDs = append(customerIDs, *log.OperatorID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.db == nil {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
accountMap, shopMap, enterpriseMap := s.loadAccountRelatedMaps(ctx, accountIDs)
|
||||||
|
customerMap := s.loadPersonalCustomerMap(ctx, customerIDs)
|
||||||
|
|
||||||
|
for _, log := range logs {
|
||||||
|
if log == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
view := result[log.ID]
|
||||||
|
if log.OperatorID == nil {
|
||||||
|
result[log.ID] = finalizeOperatorView(view, log.OperatorName, log.OperatorType, log.OperatorID)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
switch log.OperatorType {
|
||||||
|
case constants.AssetAuditOperatorTypeAdmin, constants.AssetAuditOperatorTypeAgent, constants.AssetAuditOperatorTypeEnterprise:
|
||||||
|
if account, ok := accountMap[*log.OperatorID]; ok {
|
||||||
|
view.Name = buildAccountOperatorName(account, shopMap, enterpriseMap)
|
||||||
|
}
|
||||||
|
case constants.AssetAuditOperatorTypePersonal:
|
||||||
|
if customer, ok := customerMap[*log.OperatorID]; ok {
|
||||||
|
if nickname := strings.TrimSpace(customer.Nickname); nickname != "" {
|
||||||
|
view.Name = nickname
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result[log.ID] = finalizeOperatorView(view, log.OperatorName, log.OperatorType, log.OperatorID)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) loadAccountRelatedMaps(ctx context.Context, accountIDs []uint) (map[uint]*model.Account, map[uint]string, map[uint]string) {
|
||||||
|
accountMap := make(map[uint]*model.Account)
|
||||||
|
shopMap := make(map[uint]string)
|
||||||
|
enterpriseMap := make(map[uint]string)
|
||||||
|
if len(accountIDs) == 0 || s.db == nil {
|
||||||
|
return accountMap, shopMap, enterpriseMap
|
||||||
|
}
|
||||||
|
|
||||||
|
var accounts []*model.Account
|
||||||
|
if err := s.db.WithContext(ctx).Unscoped().Where("id IN ?", accountIDs).Find(&accounts).Error; err != nil {
|
||||||
|
return accountMap, shopMap, enterpriseMap
|
||||||
|
}
|
||||||
|
|
||||||
|
shopIDs := make([]uint, 0)
|
||||||
|
shopIDSet := make(map[uint]struct{})
|
||||||
|
enterpriseIDs := make([]uint, 0)
|
||||||
|
enterpriseIDSet := make(map[uint]struct{})
|
||||||
|
|
||||||
|
for _, account := range accounts {
|
||||||
|
if account == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
accountMap[account.ID] = account
|
||||||
|
if account.ShopID != nil {
|
||||||
|
if _, exists := shopIDSet[*account.ShopID]; !exists {
|
||||||
|
shopIDSet[*account.ShopID] = struct{}{}
|
||||||
|
shopIDs = append(shopIDs, *account.ShopID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if account.EnterpriseID != nil {
|
||||||
|
if _, exists := enterpriseIDSet[*account.EnterpriseID]; !exists {
|
||||||
|
enterpriseIDSet[*account.EnterpriseID] = struct{}{}
|
||||||
|
enterpriseIDs = append(enterpriseIDs, *account.EnterpriseID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(shopIDs) > 0 {
|
||||||
|
var shops []*model.Shop
|
||||||
|
if err := s.db.WithContext(ctx).Unscoped().Where("id IN ?", shopIDs).Find(&shops).Error; err == nil {
|
||||||
|
for _, shop := range shops {
|
||||||
|
if shop == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
shopMap[shop.ID] = strings.TrimSpace(shop.ShopName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(enterpriseIDs) > 0 {
|
||||||
|
var enterprises []*model.Enterprise
|
||||||
|
if err := s.db.WithContext(ctx).Unscoped().Where("id IN ?", enterpriseIDs).Find(&enterprises).Error; err == nil {
|
||||||
|
for _, enterprise := range enterprises {
|
||||||
|
if enterprise == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
enterpriseMap[enterprise.ID] = strings.TrimSpace(enterprise.EnterpriseName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return accountMap, shopMap, enterpriseMap
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) loadPersonalCustomerMap(ctx context.Context, customerIDs []uint) map[uint]*model.PersonalCustomer {
|
||||||
|
result := make(map[uint]*model.PersonalCustomer)
|
||||||
|
if len(customerIDs) == 0 || s.db == nil {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
var customers []*model.PersonalCustomer
|
||||||
|
if err := s.db.WithContext(ctx).Unscoped().Where("id IN ?", customerIDs).Find(&customers).Error; err != nil {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, customer := range customers {
|
||||||
|
if customer == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result[customer.ID] = customer
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func finalizeOperatorView(view operatorView, storedName string, typeCode string, operatorID *uint) operatorView {
|
||||||
|
if strings.TrimSpace(view.TypeName) == "" {
|
||||||
|
view.TypeName = operatorTypeDisplayName(typeCode)
|
||||||
|
}
|
||||||
|
view.Name = resolveOperatorName(view.Name, storedName, view.TypeName, operatorID)
|
||||||
|
return view
|
||||||
|
}
|
||||||
|
|
||||||
|
func operatorTypeDisplayName(typeCode string) string {
|
||||||
|
switch strings.TrimSpace(typeCode) {
|
||||||
|
case constants.AssetAuditOperatorTypeSystem:
|
||||||
|
return "系统"
|
||||||
|
case constants.AssetAuditOperatorTypeAdmin:
|
||||||
|
return "平台用户"
|
||||||
|
case constants.AssetAuditOperatorTypeAgent:
|
||||||
|
return "代理账号"
|
||||||
|
case constants.AssetAuditOperatorTypeEnterprise:
|
||||||
|
return "企业账号"
|
||||||
|
case constants.AssetAuditOperatorTypePersonal:
|
||||||
|
return "个人客户"
|
||||||
|
default:
|
||||||
|
if strings.TrimSpace(typeCode) == "" {
|
||||||
|
return "未知"
|
||||||
|
}
|
||||||
|
return strings.TrimSpace(typeCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func fallbackResolvedOperatorName(storedName string, typeName string, operatorID *uint) string {
|
||||||
|
storedName = strings.TrimSpace(storedName)
|
||||||
|
if storedName == "" || isPlaceholderOperatorName(storedName) {
|
||||||
|
return typeNameWithOptionalID(typeName, operatorID)
|
||||||
|
}
|
||||||
|
return storedName
|
||||||
|
}
|
||||||
|
|
||||||
|
func resolveOperatorName(resolvedName string, storedName string, typeName string, operatorID *uint) string {
|
||||||
|
if strings.TrimSpace(resolvedName) != "" && !isPlaceholderOperatorName(resolvedName) {
|
||||||
|
return strings.TrimSpace(resolvedName)
|
||||||
|
}
|
||||||
|
return fallbackResolvedOperatorName(storedName, typeName, operatorID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isPlaceholderOperatorName(name string) bool {
|
||||||
|
name = strings.TrimSpace(name)
|
||||||
|
if name == "" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for _, prefix := range []string{"后台用户#", "代理用户#", "企业用户#", "个人客户#", "用户#"} {
|
||||||
|
if strings.HasPrefix(name, prefix) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func typeNameWithOptionalID(typeName string, operatorID *uint) string {
|
||||||
|
if operatorID == nil || *operatorID == 0 {
|
||||||
|
return typeName
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s(ID:%d)", typeName, *operatorID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildAccountOperatorName(account *model.Account, shopMap map[uint]string, enterpriseMap map[uint]string) string {
|
||||||
|
if account == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
username := strings.TrimSpace(account.Username)
|
||||||
|
switch account.UserType {
|
||||||
|
case constants.UserTypeAgent:
|
||||||
|
return composeReadableName(lookupNameByPtr(account.ShopID, shopMap), username)
|
||||||
|
case constants.UserTypeEnterprise:
|
||||||
|
return composeReadableName(lookupNameByPtr(account.EnterpriseID, enterpriseMap), username)
|
||||||
|
default:
|
||||||
|
return username
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func lookupNameByPtr(id *uint, nameMap map[uint]string) string {
|
||||||
|
if id == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return strings.TrimSpace(nameMap[*id])
|
||||||
|
}
|
||||||
|
|
||||||
|
func composeReadableName(primary string, secondary string) string {
|
||||||
|
primary = strings.TrimSpace(primary)
|
||||||
|
secondary = strings.TrimSpace(secondary)
|
||||||
|
switch {
|
||||||
|
case primary != "" && secondary != "" && primary != secondary:
|
||||||
|
return fmt.Sprintf("%s(%s)", primary, secondary)
|
||||||
|
case primary != "":
|
||||||
|
return primary
|
||||||
|
default:
|
||||||
|
return secondary
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import (
|
|||||||
type UserContextInfo struct {
|
type UserContextInfo struct {
|
||||||
UserID uint
|
UserID uint
|
||||||
UserType int
|
UserType int
|
||||||
|
Username string
|
||||||
ShopID uint
|
ShopID uint
|
||||||
EnterpriseID uint
|
EnterpriseID uint
|
||||||
CustomerID uint
|
CustomerID uint
|
||||||
@@ -23,11 +24,15 @@ type UserContextInfo struct {
|
|||||||
// SetUserContext 将用户信息设置到 context 中
|
// SetUserContext 将用户信息设置到 context 中
|
||||||
// 在 Auth 中间件认证成功后调用
|
// 在 Auth 中间件认证成功后调用
|
||||||
func SetUserContext(ctx context.Context, info *UserContextInfo) context.Context {
|
func SetUserContext(ctx context.Context, info *UserContextInfo) context.Context {
|
||||||
|
if info == nil {
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
ctx = context.WithValue(ctx, constants.ContextKeyUserID, info.UserID)
|
ctx = context.WithValue(ctx, constants.ContextKeyUserID, info.UserID)
|
||||||
ctx = context.WithValue(ctx, constants.ContextKeyUserType, info.UserType)
|
ctx = context.WithValue(ctx, constants.ContextKeyUserType, info.UserType)
|
||||||
ctx = context.WithValue(ctx, constants.ContextKeyShopID, info.ShopID)
|
ctx = context.WithValue(ctx, constants.ContextKeyShopID, info.ShopID)
|
||||||
ctx = context.WithValue(ctx, constants.ContextKeyEnterpriseID, info.EnterpriseID)
|
ctx = context.WithValue(ctx, constants.ContextKeyEnterpriseID, info.EnterpriseID)
|
||||||
ctx = context.WithValue(ctx, constants.ContextKeyCustomerID, info.CustomerID)
|
ctx = context.WithValue(ctx, constants.ContextKeyCustomerID, info.CustomerID)
|
||||||
|
ctx = context.WithValue(ctx, constants.ContextKeyUserInfo, info)
|
||||||
// SubordinateShopIDs: nil 表示不限制,空切片表示无权限
|
// SubordinateShopIDs: nil 表示不限制,空切片表示无权限
|
||||||
if info.SubordinateShopIDs != nil {
|
if info.SubordinateShopIDs != nil {
|
||||||
ctx = context.WithValue(ctx, constants.ContextKeySubordinateShopIDs, info.SubordinateShopIDs)
|
ctx = context.WithValue(ctx, constants.ContextKeySubordinateShopIDs, info.SubordinateShopIDs)
|
||||||
@@ -59,6 +64,18 @@ func GetUserTypeFromContext(ctx context.Context) int {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetUsernameFromContext 从 context 中提取用户名。
|
||||||
|
// 如果未设置,返回空字符串。
|
||||||
|
func GetUsernameFromContext(ctx context.Context) string {
|
||||||
|
if ctx == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if info, ok := ctx.Value(constants.ContextKeyUserInfo).(*UserContextInfo); ok && info != nil {
|
||||||
|
return info.Username
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
// GetShopIDFromContext 从 context 中提取店铺 ID
|
// GetShopIDFromContext 从 context 中提取店铺 ID
|
||||||
// 如果未设置,返回 0
|
// 如果未设置,返回 0
|
||||||
func GetShopIDFromContext(ctx context.Context) uint {
|
func GetShopIDFromContext(ctx context.Context) uint {
|
||||||
@@ -157,12 +174,16 @@ func GetRequestMethodFromContext(ctx context.Context) *string {
|
|||||||
// SetUserToFiberContext 将用户信息设置到 Fiber context 的 Locals 中
|
// SetUserToFiberContext 将用户信息设置到 Fiber context 的 Locals 中
|
||||||
// 同时也设置到标准 context 中,便于 GORM 查询使用
|
// 同时也设置到标准 context 中,便于 GORM 查询使用
|
||||||
func SetUserToFiberContext(c *fiber.Ctx, info *UserContextInfo) {
|
func SetUserToFiberContext(c *fiber.Ctx, info *UserContextInfo) {
|
||||||
|
if info == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
// 设置到 Fiber Locals
|
// 设置到 Fiber Locals
|
||||||
c.Locals(constants.ContextKeyUserID, info.UserID)
|
c.Locals(constants.ContextKeyUserID, info.UserID)
|
||||||
c.Locals(constants.ContextKeyUserType, info.UserType)
|
c.Locals(constants.ContextKeyUserType, info.UserType)
|
||||||
c.Locals(constants.ContextKeyShopID, info.ShopID)
|
c.Locals(constants.ContextKeyShopID, info.ShopID)
|
||||||
c.Locals(constants.ContextKeyEnterpriseID, info.EnterpriseID)
|
c.Locals(constants.ContextKeyEnterpriseID, info.EnterpriseID)
|
||||||
c.Locals(constants.ContextKeyCustomerID, info.CustomerID)
|
c.Locals(constants.ContextKeyCustomerID, info.CustomerID)
|
||||||
|
c.Locals(constants.ContextKeyUserInfo, info)
|
||||||
if info.SubordinateShopIDs != nil {
|
if info.SubordinateShopIDs != nil {
|
||||||
c.Locals(constants.ContextKeySubordinateShopIDs, info.SubordinateShopIDs)
|
c.Locals(constants.ContextKeySubordinateShopIDs, info.SubordinateShopIDs)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user