chore: 并入 iotlcm-app 历史到 app/(原提交 f40bda9)

This commit is contained in:
MiFaZhan
2026-11-02 13:28:00 +08:00
1107 changed files with 147143 additions and 0 deletions
@@ -0,0 +1,144 @@
package allocatedispatch
import (
"context"
"strings"
"time"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
"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/batchmetadata"
"git.5iot.com/iotlcm-public/iotlcm-app/internal/library/rabbitmq"
"git.5iot.com/iotlcm-public/iotlcm-app/internal/model/entity"
)
type message struct {
BatchNo string `json:"batchNo"`
BatchType int `json:"batchType"`
}
func Start(ctx context.Context) context.CancelFunc {
ctx, cancel := context.WithCancel(ctx)
if !g.Cfg().MustGet(ctx, "workerBatchAsync.enabled", false).Bool() || !g.Cfg().MustGet(ctx, "workerBatchAsync.allocateRecovery.enabled", true).Bool() {
return cancel
}
interval := g.Cfg().MustGet(ctx, "workerBatchAsync.allocateRecovery.intervalSeconds", 60).Int()
if interval < 30 {
interval = 30
}
go func() {
ticker := time.NewTicker(time.Duration(interval) * time.Second)
defer ticker.Stop()
var cursor int64
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
next, err := recoverPage(ctx, cursor, publish)
if err != nil {
g.Log().Errorf(ctx, "划拨任务补投失败: %+v", err)
} else {
cursor = next
}
}
}
}()
return cancel
}
func due(batch *entity.BatchMain, now time.Time, processingDelay time.Duration) bool {
if batch == nil || batch.BatchType != consts.BatchTypeAllocate || batch.SourceChannel != batchmetadata.MiniProgram {
return false
}
delay := time.Minute
switch batch.BatchAsyncStatus {
case consts.BatchAsyncStatusPending:
case consts.BatchAsyncStatusProcessing:
delay = processingDelay
default:
return false
}
timestamp := batch.UpdatedAt
if batch.BatchAsyncStatus == consts.BatchAsyncStatusProcessing && batch.StartTime != nil && !batch.StartTime.IsZero() {
timestamp = batch.StartTime
}
if timestamp == nil || timestamp.IsZero() {
timestamp = batch.CreatedAt
}
if timestamp == nil || timestamp.IsZero() {
return false
}
return !timestamp.Time.Add(delay).After(now)
}
func recoverPage(ctx context.Context, cursor int64, send func(context.Context, string, string) error) (int64, error) {
if ctx.Err() != nil {
return cursor, ctx.Err()
}
columns := dao.BatchMain.Columns()
var parents []*entity.BatchMain
err := dao.BatchMain.Ctx(ctx).Where(columns.Id+" > ?", cursor).
Where(columns.BatchType, consts.BatchTypeAllocate).Where(columns.SourceChannel, batchmetadata.MiniProgram).
Where("COALESCE(source_batch_no, '') = ''").
WhereIn(columns.BatchAsyncStatus, []int{consts.BatchAsyncStatusPending, consts.BatchAsyncStatusProcessing}).
OrderAsc(columns.Id).Limit(50).Scan(&parents)
if err != nil {
return cursor, err
}
if len(parents) == 0 {
return 0, nil
}
minutes := g.Cfg().MustGet(ctx, "workerBatchAsync.allocateRecovery.processingRetryMinutes", 30).Int()
if minutes < 30 {
minutes = 30
}
delay := time.Duration(minutes) * time.Minute
queue := strings.TrimSpace(g.Cfg().MustGet(ctx, "workerBatchAsync.queueName", "iotlcm.batch.execute").String())
if queue == "" {
queue = "iotlcm.batch.execute"
}
for _, parent := range parents {
if ctx.Err() != nil {
return cursor, ctx.Err()
}
cursor = parent.Id
if parent.BatchInfo == nil || parent.BatchInfo.Get("jobRole").String() != "allocate_parent" || !due(parent, time.Now(), delay) {
continue
}
var children []*entity.BatchMain
err = dao.BatchMain.Ctx(ctx).Where(columns.SourceBatchNo, parent.BatchNo).
Where(columns.BatchType, consts.BatchTypeAllocate).Where(columns.TenantId, parent.TenantId).
Where(columns.CreatedBy, parent.CreatedBy).Where(columns.SourceChannel, batchmetadata.MiniProgram).Scan(&children)
if err != nil {
return cursor, err
}
for _, child := range children {
if child.BatchInfo == nil || child.BatchInfo.Get("jobRole").String() != "allocate_chunk" || !due(child, time.Now(), delay) {
continue
}
if err := send(ctx, queue, child.BatchNo); err != nil {
g.Log().Errorf(ctx, "划拨子任务补投失败 batchNo:%s err:%+v", child.BatchNo, err)
}
}
if err := send(ctx, queue, parent.BatchNo); err != nil {
g.Log().Errorf(ctx, "划拨父任务补投失败 batchNo:%s err:%+v", parent.BatchNo, err)
}
}
if len(parents) < 50 {
return 0, nil
}
return cursor, nil
}
func publish(ctx context.Context, queue, batchNo string) error {
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
err := rabbitmq.Publish(ctx, queue, message{BatchNo: batchNo, BatchType: consts.BatchTypeAllocate})
if err == nil {
g.Log().Infof(ctx, "划拨持久任务补投 batchNo:%s time:%s", batchNo, gtime.Now())
}
return err
}
@@ -0,0 +1,77 @@
package allocatedispatch
import (
"context"
"errors"
_ "github.com/gogf/gf/contrib/drivers/sqlite/v2"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
"testing"
"time"
"git.5iot.com/iotlcm-public/iotlcm-app/internal/model/entity"
)
func TestDue(t *testing.T) {
now := time.Now()
for _, test := range []struct {
status int
age time.Duration
due bool
}{{1, 59 * time.Second, false}, {1, 2 * time.Minute, true}, {2, 29 * time.Minute, false}, {2, 31 * time.Minute, true}, {3, time.Hour, false}, {4, time.Hour, false}, {5, time.Hour, false}} {
batch := &entity.BatchMain{BatchType: 3, SourceChannel: "mini_program", BatchAsyncStatus: test.status, UpdatedAt: gtime.New(now.Add(-test.age))}
if due(batch, now, 30*time.Minute) != test.due {
t.Fatal(test)
}
}
if due(&entity.BatchMain{BatchType: 3, SourceChannel: "console", BatchAsyncStatus: 1, UpdatedAt: gtime.New(now.Add(-time.Hour))}, now, 30*time.Minute) {
t.Fatal("foreign channel replayed")
}
if due(&entity.BatchMain{BatchType: 3, SourceChannel: "mini_program", BatchAsyncStatus: 1}, now, 30*time.Minute) {
t.Fatal("missing time replayed")
}
}
func TestRecoveryPersistsFailureAndIsolatesParents(t *testing.T) {
ctx := context.Background()
if err := gdb.SetConfig(gdb.Config{"default": gdb.ConfigGroup{{Type: "sqlite", Link: "sqlite::@file(:memory:)", MaxOpenConnCount: 1}}}); err != nil {
t.Fatal(err)
}
t.Cleanup(func() { _ = g.DB().Close(ctx) })
for _, sql := range []string{
`CREATE TABLE batch_main (id INTEGER PRIMARY KEY,batch_no TEXT,batch_type INTEGER,source_batch_no TEXT,source_channel TEXT,batch_async_status INTEGER,tenant_id TEXT,created_by INTEGER,batch_info TEXT,created_at TEXT,updated_at TEXT,start_time TEXT)`,
`INSERT INTO batch_main VALUES (1,'PARENT',3,'','mini_program',1,'A',7,'{"jobRole":"allocate_parent"}','2020-01-01','2020-01-01',NULL), (2,'CHILD',3,'PARENT','mini_program',1,'A',7,'{"jobRole":"allocate_chunk"}','2020-01-01','2020-01-01',NULL), (3,'FOREIGN',3,'PARENT','mini_program',1,'B',7,'{"jobRole":"allocate_chunk"}','2020-01-01','2020-01-01',NULL), (4,'DONE',3,'','mini_program',3,'A',7,'{"jobRole":"allocate_parent"}','2020-01-01','2020-01-01',NULL), (5,'OTHER',101,'','mini_program',1,'A',7,'{}','2020-01-01','2020-01-01',NULL), (6,'CONSOLE',3,'','console',1,'A',7,'{"jobRole":"allocate_parent"}','2020-01-01','2020-01-01',NULL)`,
} {
if _, err := g.DB().Exec(ctx, sql); err != nil {
t.Fatal(err)
}
}
for _, fail := range []bool{true, false} {
var calls []string
cursor, err := recoverPage(ctx, 0, func(ctx context.Context, queue, batchNo string) error {
calls = append(calls, batchNo)
if queue != "iotlcm.batch.execute" {
t.Fatal(queue)
}
if fail {
return errors.New("queue offline")
}
return nil
})
if err != nil || cursor != 0 || len(calls) != 2 || calls[0] != "CHILD" || calls[1] != "PARENT" {
t.Fatal(calls, cursor, err)
}
}
if _, err := g.DB().Exec(ctx, `UPDATE batch_main SET batch_async_status=3 WHERE batch_no='PARENT'`); err != nil {
t.Fatal(err)
}
_, err := recoverPage(ctx, 0, func(context.Context, string, string) error { t.Fatal("terminal parent replayed"); return nil })
if err != nil {
t.Fatal(err)
}
cancelled, cancel := context.WithCancel(ctx)
cancel()
if _, err := recoverPage(cancelled, 0, func(context.Context, string, string) error { t.Fatal("cancelled replay"); return nil }); err == nil {
t.Fatal("cancel ignored")
}
}
+203
View File
@@ -0,0 +1,203 @@
// ================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// You can delete these comments if you wish manually maintain this interface file.
// ================================================================================
package service
import (
"context"
"git.5iot.com/iotlcm-public/iotlcm-app/internal/model"
"git.5iot.com/iotlcm-public/iotlcm-app/internal/model/entity"
)
type (
IAppAuth interface {
Captcha(ctx context.Context) (captchaID string, captchaImage string, err error)
SideCaptcha(ctx context.Context) (res *model.AppSideCaptchaModel, err error)
CheckSideCaptcha(ctx context.Context, param *model.AppCheckSideCaptchaParam) (err error)
SendSms(ctx context.Context, param *model.AppSendSmsParam) (res *model.AppSendSmsModel, err error)
PhoneRegister(ctx context.Context, param *model.AppPhoneRegisterParam) (res *model.AppAuthTokenModel, err error)
PhoneLogin(ctx context.Context, param *model.AppPhoneLoginParam) (res *model.AppAuthTokenModel, err error)
PhoneSmsLogin(ctx context.Context, param *model.AppPhoneSmsLoginParam) (res *model.AppAuthTokenModel, err error)
WechatLogin(ctx context.Context, param *model.AppWechatLoginParam) (res *model.AppWechatLoginModel, err error)
WechatBindPhone(ctx context.Context, param *model.AppWechatBindPhoneParam) (res *model.AppWechatBindPhoneModel, err error)
BindWechatOpenIdByLoginCode(ctx context.Context, user *entity.SysUser, wxCode string) error
ForgotPassword(ctx context.Context, param *model.AppForgotPasswordParam) (err error)
}
IAppCard interface {
// BalanceRechargeSubmit 创建余额充值待支付订单,并返回前端可使用的支付信息。
// 余额入账和余额流水只在支付回调或模拟支付确认成功后写入。
BalanceRechargeSubmit(ctx context.Context, param *model.AppBalanceRechargeSubmitParam) (res *model.AppBalanceRechargeSubmitModel, err error)
BalanceRechargeMockPay(ctx context.Context, param *model.AppBalanceRechargeMockPayParam) (*model.AppBalanceRechargeStatusModel, error)
BalanceRechargeStatus(ctx context.Context, param *model.AppBalanceRechargeStatusParam) (*model.AppBalanceRechargeStatusModel, error)
BalanceRechargeRefund(ctx context.Context, param *model.AppBalanceRechargeRefundParam) (*model.AppOrderModel, error)
// AllocateTargets 查询当前企业用户可划拨的目标客户。
AllocateTargets(ctx context.Context, param *model.AppCardAllocateTargetListParam) (items []*model.AppCardAllocateTargetModel, total int, err error)
// AllocateBatchPrepare 校验待划拨卡片,并写入划拨预处理批次。
AllocateBatchPrepare(ctx context.Context, param *model.AppCardAllocateBatchPrepareParam) (res *model.AppCardAllocateBatchModel, err error)
// AllocateBatchSubmit 确认划拨预处理批次,并更新卡片归属;划拨后原企业的可见和办理权限仍保留。
AllocateBatchSubmit(ctx context.Context, param *model.AppCardAllocateBatchSubmitParam) (res *model.AppCardAllocateBatchModel, err error)
Bind(ctx context.Context, param *model.AppCardBindParam) (res *model.AppCardModel, err error)
// ChangePrepare 校验企业用户套餐变更条件,并写入预处理批次。
ChangePrepare(ctx context.Context, param *model.AppCardChangePrepareParam) (res *model.AppCardChangePrepareModel, err error)
// ChangeSubmit 确认套餐变更预处理批次,并在同一事务内完成余额扣款和本地套餐变更。
ChangeSubmit(ctx context.Context, param *model.AppCardChangeSubmitParam) (res *model.AppCardChangeSubmitModel, err error)
// 查询当前登录用户可见的套餐订购记录。
PackageList(ctx context.Context, param *model.AppPackageListParam) (items []*model.AppPackageModel, total int, err error)
// 查询卡片当前套餐详情。
// 核心规则是“卡主表 current_package_code -> 套餐表 package_code”,订购记录只做详情兜底,
// 避免卡主表与订购表不同步时前端报“套餐信息不存在”。
PublicPackageDetail(ctx context.Context, param *model.AppPackagePublicDetailParam) (res *model.AppPackageModel, err error)
// 处理首页免登录公共查卡,只返回卡基础信息,不开放绑卡、重启等业务能力。
PublicQuery(ctx context.Context, param *model.AppCardQueryParam) (res *model.AppCardModel, err error)
// 查询当前登录用户可见卡片。终端用户走 t_sim_terminal 关联表,
// 企业用户沿用管理平台数据权限查询持卡列表。
List(ctx context.Context, param *model.AppCardListParam) (items []*model.AppCardModel, total int, err error)
// 登录后先查用户可见卡;查不到时回退公共查卡,保证登录后仍能查看公共卡详情。
Detail(ctx context.Context, param *model.AppCardDetailParam) (res *model.AppCardModel, err error)
// RenewPrepare 根据当前套餐 renewable_duration_config 生成可续费年限档位,
// 再按当前用户身份解析套餐单年价,返回每个档位的续费总价。
RenewPrepare(ctx context.Context, param *model.AppCardRenewPrepareParam) (res *model.AppCardRenewPrepareModel, err error)
// RenewSubmit 使用账户余额支付,并把单卡续费也写入正式续费批次。
RenewSubmit(ctx context.Context, param *model.AppCardRenewSubmitParam) (res *model.AppCardRenewSubmitModel, err error)
// 批量续费约束做预校验:
// 同一批次必须同当前套餐、同客户归属、同定价,才能生成统一续费档位。
RenewBatchPrepare(ctx context.Context, param *model.AppCardRenewBatchPrepareParam) (res *model.AppCardRenewPrepareModel, err error)
// RenewBatchSubmit 确认续费预处理批次,并在同一事务内完成余额扣款和批量续费落库。
// 批量续费必须先走预处理校验,避免卡片、套餐或价格在提交时发生变化。
RenewBatchSubmit(ctx context.Context, param *model.AppCardRenewBatchSubmitParam) (res *model.AppCardRenewSubmitModel, err error)
// Restart 兼容原重启接口。启用 worker 后停机、复机和重启都投递批次异步执行。
Restart(ctx context.Context, param *model.AppCardRestartParam) (res *model.AppCardRestartModel, err error)
Stop(ctx context.Context, param *model.AppCardRestartParam) (res *model.AppCardLifecycleModel, err error)
Start(ctx context.Context, param *model.AppCardRestartParam) (res *model.AppCardLifecycleModel, err error)
// LifecycleBatchSubmit 提交卡片停机、复机和重启批次。
// 终端用户仅允许重启;停机和复机仍然只开放给企业用户。
LifecycleBatchSubmit(ctx context.Context, param *model.AppCardLifecycleParam) (res *model.AppCardLifecycleModel, err error)
LifecycleBatchStatus(ctx context.Context, param *model.AppCardLifecycleStatusParam) (res *model.AppCardLifecycleModel, err error)
BalanceRechargeWechatNotify(ctx context.Context) error
BalanceRechargeWechatRefundNotify(ctx context.Context) error
RecognizeCard(ctx context.Context) (res *model.AppCardRecognitionModel, err error)
}
IAppUser interface {
WarningPreference(ctx context.Context) (*model.AppWarningPreferenceModel, error)
SaveWarningPreference(ctx context.Context, param *model.AppWarningPreferenceParam) (*model.AppWarningPreferenceModel, error)
UnbindWechat(ctx context.Context) (*model.AppWechatBindModel, error)
CustomerList(ctx context.Context, param *model.AppCustomerListParam) (items []*model.AppCustomerModel, total int, summary *model.AppCustomerSummary, err error)
CustomerDetail(ctx context.Context, param *model.AppCustomerDetailParam) (*model.AppCustomerDetailModel, error)
Info(ctx context.Context) (res *model.AppUserInfoModel, err error)
UpdateInfo(ctx context.Context, param *model.AppUpdateUserInfoParam) (res *model.AppUserInfoModel, err error)
BindWechat(ctx context.Context, param *model.AppWechatBindParam) (res *model.AppWechatBindModel, err error)
}
IAppMessage interface {
List(ctx context.Context, param *model.AppMessageListParam) (items []*model.AppMessageModel, total int, err error)
Detail(ctx context.Context, param *model.AppMessageDetailParam) (*model.AppMessageModel, error)
Read(ctx context.Context, param *model.AppMessageReadParam) error
}
IAppOrder interface {
List(ctx context.Context, param *model.AppOrderListParam) (items []*model.AppOrderModel, total int, err error)
Detail(ctx context.Context, param *model.AppOrderDetailParam) (*model.AppOrderModel, error)
}
IAppWarning interface {
ConfigList(ctx context.Context, param *model.AppWarningConfigListParam) (res *model.AppWarningConfigListModel, err error)
ConfigMeta(ctx context.Context) (res *model.AppWarningConfigMetaModel, err error)
ConfigSave(ctx context.Context, param *model.AppWarningConfigSaveParam) (res *model.AppWarningConfigSaveModel, err error)
ConfigBatchSave(ctx context.Context, param *model.AppWarningConfigBatchSaveParam) (res *model.AppWarningConfigBatchSaveModel, err error)
ConfigNotify(ctx context.Context, param *model.AppWarningConfigNotifyParam) (res *model.AppWarningConfigNotifyModel, err error)
ConfigBatchNotify(ctx context.Context, param *model.AppWarningConfigBatchNotifyParam) (res *model.AppWarningConfigBatchNotifyModel, err error)
}
IAppWorkOrder interface {
Action(ctx context.Context, param *model.AppWorkOrderActionParam) error
Upload(ctx context.Context) (output *model.SysOssUploadModel, err error)
List(ctx context.Context, param *model.AppWorkOrderListParam) (items []*model.AppWorkOrderModel, total int, err error)
Detail(ctx context.Context, param *model.AppWorkOrderDetailParam) (res *model.AppWorkOrderModel, err error)
Create(ctx context.Context, param *model.AppWorkOrderCreateParam) (res *model.AppWorkOrderModel, err error)
}
)
var (
localAppAuth IAppAuth
localAppCard IAppCard
localAppUser IAppUser
localAppMessage IAppMessage
localAppOrder IAppOrder
localAppWarning IAppWarning
localAppWorkOrder IAppWorkOrder
)
func AppAuth() IAppAuth {
if localAppAuth == nil {
panic("implement not found for interface IAppAuth, forgot register?")
}
return localAppAuth
}
func RegisterAppAuth(i IAppAuth) {
localAppAuth = i
}
func AppCard() IAppCard {
if localAppCard == nil {
panic("implement not found for interface IAppCard, forgot register?")
}
return localAppCard
}
func RegisterAppCard(i IAppCard) {
localAppCard = i
}
func AppUser() IAppUser {
if localAppUser == nil {
panic("implement not found for interface IAppUser, forgot register?")
}
return localAppUser
}
func RegisterAppUser(i IAppUser) {
localAppUser = i
}
func AppMessage() IAppMessage {
if localAppMessage == nil {
panic("implement not found for interface IAppMessage, forgot register?")
}
return localAppMessage
}
func RegisterAppMessage(i IAppMessage) {
localAppMessage = i
}
func AppOrder() IAppOrder {
if localAppOrder == nil {
panic("implement not found for interface IAppOrder, forgot register?")
}
return localAppOrder
}
func RegisterAppOrder(i IAppOrder) {
localAppOrder = i
}
func AppWarning() IAppWarning {
if localAppWarning == nil {
panic("implement not found for interface IAppWarning, forgot register?")
}
return localAppWarning
}
func RegisterAppWarning(i IAppWarning) {
localAppWarning = i
}
func AppWorkOrder() IAppWorkOrder {
if localAppWorkOrder == nil {
panic("implement not found for interface IAppWorkOrder, forgot register?")
}
return localAppWorkOrder
}
func RegisterAppWorkOrder(i IAppWorkOrder) {
localAppWorkOrder = i
}
@@ -0,0 +1,26 @@
package service
import (
"context"
"git.5iot.com/iotlcm-public/iotlcm-app/internal/model"
)
type IAppDiagnosis interface {
Info(ctx context.Context, param *model.AppDiagnosisInfoParam) (res *model.AppDiagnosisInfoModel, err error)
Flow(ctx context.Context, param *model.AppDiagnosisFlowParam) (res *model.AppDiagnosisFlowModel, err error)
OpenInfo(ctx context.Context, param *model.AppOpenDiagnosisInfoParam) (res *model.AppDiagnosisInfoModel, err error)
OpenFlow(ctx context.Context, param *model.AppOpenDiagnosisFlowParam) (res *model.AppDiagnosisFlowModel, err error)
}
var localAppDiagnosis IAppDiagnosis
func AppDiagnosis() IAppDiagnosis {
if localAppDiagnosis == nil {
panic("implement not found for interface IAppDiagnosis, forgot register?")
}
return localAppDiagnosis
}
func RegisterAppDiagnosis(i IAppDiagnosis) {
localAppDiagnosis = i
}
@@ -0,0 +1,303 @@
package executionlog
import (
"context"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/util/gconv"
"regexp"
"slices"
"strings"
"unicode/utf8"
"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/allocationunits"
"git.5iot.com/iotlcm-public/iotlcm-app/internal/library/batchmetadata"
"git.5iot.com/iotlcm-public/iotlcm-app/internal/library/contexts"
"git.5iot.com/iotlcm-public/iotlcm-app/internal/library/xgorm/handler"
"git.5iot.com/iotlcm-public/iotlcm-app/internal/model"
)
// 办理结果只允许发起人查看;通用批次日志沿用控制台的数据范围。
type ownerOnlyKey struct{}
func scope(ctx context.Context) (*gdb.Model, error) {
if contexts.GetUserId(ctx) <= 0 || strings.TrimSpace(contexts.GetTenantId(ctx)) == "" {
return nil, gerror.New("请先登录")
}
return scopedModel(ctx, dao.BatchMain.Ctx(ctx))
}
func scopedModel(ctx context.Context, mod *gdb.Model) (*gdb.Model, error) {
user, tenant := contexts.GetUserId(ctx), strings.TrimSpace(contexts.GetTenantId(ctx))
if user <= 0 || tenant == "" {
return nil, gerror.New("请先登录")
}
mod = mod.Where("tenant_id", tenant)
if ownerOnly, _ := ctx.Value(ownerOnlyKey{}).(bool); ownerOnly {
return mod.Where("created_by", user), nil
}
if contexts.IsSuperAdmin(ctx) {
return mod, nil
}
// 菜单范围由认证中间件读取当前数据库后写入,优先于登录时的角色快照。
roleId := gconv.Int64(contexts.GetDataValue(ctx, "currentRoleId"))
dataScope := gconv.String(contexts.GetDataValue(ctx, "currentRoleDataScope"))
if roleId > 0 && dataScope != "" && len(contexts.GetRoles(ctx)) == 0 {
identity := *contexts.GetUser(ctx)
identity.Roles = []model.Role{{RoleId: roleId, DataScope: dataScope}}
copied := *contexts.Get(ctx)
copied.User = &identity
ctx = contexts.Set(ctx, &copied)
mod = mod.Ctx(ctx)
}
if len(contexts.GetRoles(ctx)) == 0 {
return mod.Where("1 = 0"), nil
}
validScopes := []string{
consts.SysRoleDataScopeAll, consts.SysRoleDataScopeCustom,
consts.SysRoleDataScopeDept, consts.SysRoleDataScopeDeptAndBelow,
consts.SysRoleDataScopePersonal, consts.SysRoleDataScopeDeptAndBelowOrPersonal,
consts.SysRoleDataScopeOrgAndNextLevel, consts.SysRoleDataScopeOrgNextLevel,
}
if roleId > 0 && dataScope != "" {
if !slices.Contains(validScopes, dataScope) {
return mod.Where("1 = 0"), nil
}
} else {
for _, role := range contexts.GetRoles(ctx) {
if !slices.Contains(validScopes, role.DataScope) {
return mod.Where("1 = 0"), nil
}
}
}
// 先生成创建部门/创建人的过滤条件,再选择返回字段,避免 DTO 缺少权限字段导致漏过滤。
return handler.FilterAuth(mod.Fields("created_dept", "created_by")), nil
}
func dbError(ctx context.Context, err error) error {
g.Log().Error(ctx, "execution log query failed", err)
return gerror.New("执行日志查询失败,请稍后重试")
}
func paging(page, size int) (int, int) {
if page < 1 {
page = 1
}
if size < 1 {
size = 20
}
if size > 100 {
size = 100
}
return page, size
}
func normalize(item *model.AppExecutionLogItem) {
item.SourceChannel = batchmetadata.Channel(item.SourceChannel)
if !batchmetadata.ValidMode(item.ExecutionMode) {
item.ExecutionMode = batchmetadata.Unknown
}
item.FailReason = safeReason(item.FailReason)
}
// Do not expose internal network addresses, credentials or stack traces to mobile clients.
var publicErrorCode = regexp.MustCompile("^[A-Za-z0-9_.-]{1,64}$")
func safeReason(value string) string {
value = strings.TrimSpace(value)
if value == "" {
return ""
}
lower := strings.ToLower(value)
for _, marker := range []string{"token", "secret", "password", "authorization", "api_key", "apikey", "http:", "https:", "rpc", "sql", "tcp", "panic", "goroutine", "stack", "/src/", "/app/", "dial ", "127.0.", "192." + "168.", "10.", "172.", "sk-", "mysql", "redis"} {
if strings.Contains(lower, marker) {
return "执行失败,请凭批次号联系客服核查"
}
}
if strings.ContainsAny(value, "\r\n") || utf8.RuneCountInString(value) > 180 {
return "执行失败,请凭批次号联系客服核查"
}
return value
}
func List(ctx context.Context, in *model.AppExecutionLogListParam) ([]*model.AppExecutionLogItem, int, error) {
mod, err := scope(ctx)
if err != nil {
return nil, 0, err
}
if in == nil {
in = &model.AppExecutionLogListParam{}
}
c := dao.BatchMain.Columns()
mod = mod.Fields(model.AppExecutionLogItem{})
if n := strings.TrimSpace(in.BatchNo); n != "" {
mod = mod.Where(c.BatchNo, n)
}
if in.SourceChannel != "" {
mod = mod.Where(c.SourceChannel, in.SourceChannel)
}
if in.ExecutionMode != nil {
mod = mod.Where(c.ExecutionMode, *in.ExecutionMode)
}
if in.SourceType != nil {
mod = mod.Where(c.SourceType, *in.SourceType)
}
if in.BatchAsyncStatus != nil {
mod = mod.Where(c.BatchAsyncStatus, *in.BatchAsyncStatus)
}
// Split allocation children are execution details, not a second user submission.
// Allocation children are execution details of the parent submission. The
// source_batch_no relation is authoritative; batch_info is only descriptive.
mod = mod.Where("NOT (batch_type = ? AND COALESCE(source_batch_no, '') <> '' AND source_batch_no <> batch_no)", consts.BatchTypeAllocate)
page, size := paging(in.Page, in.PageSize)
items := make([]*model.AppExecutionLogItem, 0)
total := 0
if err = mod.Page(page, size).OrderDesc(c.Id).ScanAndCount(&items, &total, false); err != nil {
return nil, 0, dbError(ctx, err)
}
unitFields := []*allocationunits.Fields{}
for _, item := range items {
normalize(item)
if item.BatchType == consts.BatchTypeAllocate {
item.Fields = allocationunits.Resolve(item.UnitInfo, item.StoredSourceDeptId, item.StoredTargetDeptId)
unitFields = append(unitFields, &item.Fields)
}
}
if err := allocationunits.Fill(ctx, contexts.GetTenantId(ctx), unitFields); err != nil {
return nil, 0, dbError(ctx, err)
}
return items, total, nil
}
func ensureAllocateParent(ctx context.Context, batchNo string) error {
mod, err := scope(ctx)
if err != nil {
return err
}
columns := dao.BatchMain.Columns()
count, err := mod.Where(columns.BatchNo, strings.TrimSpace(batchNo)).Where("(batch_type = ? OR (batch_type = ? AND JSON_EXTRACT(batch_info, '$.targetBatchType') = ?))", consts.BatchTypeAllocate, consts.BatchTypePrepare, consts.BatchTypeAllocate).Where("COALESCE(source_batch_no, '') = ''").Count()
if err != nil {
return dbError(ctx, err)
}
if count != 1 {
return gerror.New("划拨批次不存在或无权查看")
}
return nil
}
func AllocateSummary(ctx context.Context, batchNo string) (*model.AppExecutionLogItem, error) {
ctx = context.WithValue(ctx, ownerOnlyKey{}, true)
if err := ensureAllocateParent(ctx, batchNo); err != nil {
return nil, err
}
return Summary(ctx, batchNo)
}
func AllocateDetails(ctx context.Context, in *model.AppExecutionLogDetailParam) ([]*model.AppExecutionLogDetail, int, error) {
ctx = context.WithValue(ctx, ownerOnlyKey{}, true)
if in == nil {
return nil, 0, gerror.New("批次号不能为空")
}
if err := ensureAllocateParent(ctx, in.BatchNo); err != nil {
return nil, 0, err
}
return Details(ctx, in)
}
func Summary(ctx context.Context, batchNo string) (*model.AppExecutionLogItem, error) {
mod, err := scope(ctx)
if err != nil {
return nil, err
}
if strings.TrimSpace(batchNo) == "" {
return nil, gerror.New("批次号不能为空")
}
var item *model.AppExecutionLogItem
if err = mod.Fields(model.AppExecutionLogItem{}).
Where(dao.BatchMain.Columns().BatchNo, batchNo).
Where("NOT (batch_type = ? AND COALESCE(source_batch_no, '') <> '' AND source_batch_no <> batch_no)", consts.BatchTypeAllocate).
Scan(&item); err != nil {
return nil, dbError(ctx, err)
}
if item == nil {
return nil, gerror.New("执行日志不存在或无权查看")
}
normalize(item)
if item.BatchType == consts.BatchTypeAllocate {
item.Fields = allocationunits.Resolve(item.UnitInfo, item.StoredSourceDeptId, item.StoredTargetDeptId)
if err := allocationunits.Fill(ctx, contexts.GetTenantId(ctx), []*allocationunits.Fields{&item.Fields}); err != nil {
return nil, dbError(ctx, err)
}
}
return item, nil
}
func Details(ctx context.Context, in *model.AppExecutionLogDetailParam) ([]*model.AppExecutionLogDetail, int, error) {
if in == nil {
return nil, 0, gerror.New("批次号不能为空")
}
parent, err := Summary(ctx, in.BatchNo)
if err != nil {
return nil, 0, err
}
c := dao.BatchDetail.Columns()
mod, err := scopedModel(ctx, dao.BatchDetail.Ctx(ctx))
if err != nil {
return nil, 0, err
}
mod = mod.Fields(model.AppExecutionLogDetail{})
if parent.BatchType == consts.BatchTypeAllocate {
// 先按当前菜单的数据范围解析子批次,再用批次号索引查询明细。
var children []struct {
BatchNo string `orm:"batch_no"`
}
mainColumns := dao.BatchMain.Columns()
childrenMod, err := scope(ctx)
if err != nil {
return nil, 0, err
}
if err := childrenMod.Fields(mainColumns.BatchNo).
Where(mainColumns.SourceBatchNo, in.BatchNo).Where(mainColumns.BatchType, parent.BatchType).
Where(mainColumns.BatchNo+" <> ?", in.BatchNo).Scan(&children); err != nil {
return nil, 0, dbError(ctx, err)
}
batchNos := []string{in.BatchNo}
for _, child := range children {
batchNos = append(batchNos, child.BatchNo)
}
mod = mod.WhereIn(c.BatchNo, batchNos)
} else {
mod = mod.Where(c.BatchNo, in.BatchNo)
}
if in.DetailStatus != nil {
mod = mod.Where(c.DetailStatus, *in.DetailStatus)
}
page, size := paging(in.Page, in.PageSize)
items := make([]*model.AppExecutionLogDetail, 0)
total := 0
if err = mod.OrderAsc(c.Id).Page(page, size).ScanAndCount(&items, &total, false); err != nil {
return nil, 0, dbError(ctx, err)
}
unitFields := []*allocationunits.Fields{}
for index, item := range items {
if parent.BatchType == consts.BatchTypeAllocate {
item.Fields = allocationunits.Resolve(item.UnitInfo, parent.SourceDeptId, parent.TargetDeptId)
if item.SourceDeptName == "" && item.SourceDeptId == parent.SourceDeptId {
item.SourceDeptName = parent.SourceDeptName
}
if item.TargetDeptName == "" && item.TargetDeptId == parent.TargetDeptId {
item.TargetDeptName = parent.TargetDeptName
}
unitFields = append(unitFields, &item.Fields)
}
item.RowNo = (page-1)*size + index + 1
item.ErrorReason = safeReason(item.ErrorReason)
if item.ErrorCode != "" && !publicErrorCode.MatchString(item.ErrorCode) {
item.ErrorCode = "EXECUTION_FAILED"
}
}
if err := allocationunits.Fill(ctx, contexts.GetTenantId(ctx), unitFields); err != nil {
return nil, 0, dbError(ctx, err)
}
return items, total, nil
}
@@ -0,0 +1,251 @@
package executionlog
import (
"context"
_ "github.com/gogf/gf/contrib/drivers/sqlite/v2"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
"testing"
"git.5iot.com/iotlcm-public/iotlcm-app/internal/consts"
"git.5iot.com/iotlcm-public/iotlcm-app/internal/library/contexts"
"git.5iot.com/iotlcm-public/iotlcm-app/internal/model"
)
func TestPrincipalIsolation(t *testing.T) {
ctx := context.Background()
if _, _, err := List(ctx, nil); err == nil {
t.Fatal("guest allowed")
}
if _, err := Summary(ctx, "mine"); err == nil {
t.Fatal("guest summary allowed")
}
if _, _, err := Details(ctx, &model.AppExecutionLogDetailParam{BatchNo: "mine"}); err == nil {
t.Fatal("guest details allowed")
}
if err := gdb.SetConfig(gdb.Config{"default": gdb.ConfigGroup{{Type: "sqlite", Link: "sqlite::@file(:memory:)", MaxOpenConnCount: 1}}}); err != nil {
t.Fatal(err)
}
t.Cleanup(func() { _ = g.DB().Close(ctx) })
for _, sql := range []string{
"CREATE TABLE batch_main (id INTEGER PRIMARY KEY, batch_no TEXT, batch_type INTEGER, source_batch_no TEXT, source_channel TEXT, execution_mode INTEGER, source_type INTEGER, batch_async_status INTEGER, total_count INTEGER, run_result INTEGER, fail_reason TEXT, created_at TEXT, start_time TEXT, end_time TEXT, tenant_id TEXT, created_by INTEGER, created_dept INTEGER DEFAULT 10, target_dept_id INTEGER DEFAULT 0, batch_info TEXT)",
"CREATE TABLE batch_detail (id INTEGER PRIMARY KEY, batch_no TEXT, row_no INTEGER, item_id TEXT, detail_status INTEGER, error_code TEXT, error_reason TEXT, tenant_id TEXT, created_by INTEGER, created_dept INTEGER DEFAULT 10, snapshot TEXT)",
"INSERT INTO batch_main(id,batch_no,batch_type,tenant_id,created_by,source_channel,execution_mode,source_type) VALUES (1,'mine',101,'A',7,'mini_program',1,0),(2,'other-user',101,'A',8,'console',2,3),(3,'other-tenant',101,'B',7,'console',2,3)",
"INSERT INTO batch_detail(id,batch_no,row_no,item_id,detail_status,error_code,error_reason,tenant_id,created_by) VALUES(1,'mine',1,'card1',3,'ERR','套餐已过期','A',7),(2,'mine',2,'hidden',3,'ERR','hidden','B',7)",
} {
if _, err := g.DB().Exec(ctx, sql); err != nil {
t.Fatal(err)
}
}
ctx = contexts.Set(ctx, &model.Context{User: &model.Identity{BaseClaims: model.BaseClaims{ID: 7, DeptId: 10, TenantId: "A", Roles: []model.Role{{RoleId: 82, DataScope: consts.SysRoleDataScopePersonal}}}}})
list, total, err := List(ctx, nil)
if err != nil || total != 1 || len(list) != 1 || list[0].BatchNo != "mine" {
t.Fatalf("list=%+v total=%d err=%v", list, total, err)
}
for _, no := range []string{"other-user", "other-tenant", "missing"} {
if _, err := Summary(ctx, no); err == nil {
t.Fatalf("summary exposed %s", no)
}
if _, _, err := Details(ctx, &model.AppExecutionLogDetailParam{BatchNo: no}); err == nil {
t.Fatalf("details exposed %s", no)
}
}
details, total, err := Details(ctx, &model.AppExecutionLogDetailParam{BatchNo: "mine"})
if err != nil || total != 1 || len(details) != 1 || details[0].ItemId != "card1" {
t.Fatalf("details=%+v total=%d err=%v", details, total, err)
}
source := 0
list, total, err = List(ctx, &model.AppExecutionLogListParam{SourceType: &source, SourceChannel: "mini_program"})
if err != nil || total != 1 || len(list) != 1 {
t.Fatal("zero filter failed", err, total)
}
source = 3
_, total, err = List(ctx, &model.AppExecutionLogListParam{SourceType: &source})
if err != nil || total != 0 {
t.Fatal("foreign records leaked through filter", err, total)
}
t.Run("控制台数据范围", func(t *testing.T) {
for _, sql := range []string{
"CREATE TABLE sys_dept (dept_id INTEGER PRIMARY KEY, parent_id INTEGER, dept_type INTEGER, ancestors TEXT,tenant_id TEXT,dept_key TEXT,dept_name TEXT,deleted_at TEXT)",
"CREATE TABLE sys_role_dept (tenant_id TEXT, role_id INTEGER, dept_id INTEGER)",
"INSERT INTO sys_dept(dept_id,parent_id,dept_type,ancestors) VALUES(10,0,1,',0,'),(20,10,1,',0,10,'),(30,20,1,',0,10,20,'),(40,0,1,',0,')",
"UPDATE sys_dept SET tenant_id='A',dept_name='部门',dept_key='key'",
"INSERT INTO sys_role_dept VALUES('A',82,20),('B',82,40)",
"UPDATE batch_main SET created_dept=20 WHERE batch_no='other-user'",
"INSERT INTO batch_main(id,batch_no,batch_type,tenant_id,created_by,created_dept) VALUES(20,'grandchild',101,'A',9,30),(21,'outside',101,'A',10,40)",
"INSERT INTO batch_detail(id,batch_no,row_no,item_id,detail_status,tenant_id,created_by,created_dept) VALUES(50,'other-user',1,'other-card',2,'A',8,20)",
} {
if _, err := g.DB().Exec(ctx, sql); err != nil {
t.Fatal(err)
}
}
t.Cleanup(func() {
_, _ = g.DB().Exec(ctx, "DELETE FROM batch_main WHERE id IN (20,21)")
_, _ = g.DB().Exec(ctx, "DELETE FROM batch_detail WHERE id=50")
})
cases := []struct {
name, roleScope, menuScope string
dept, role int64
count int
super bool
}{
{name: "仅本人", roleScope: "5", dept: 10, role: 82, count: 1},
{name: "本部门", roleScope: "3", dept: 10, role: 82, count: 1},
{name: "本部门及下级", roleScope: "4", dept: 10, role: 82, count: 3},
{name: "自定义部门", roleScope: "2", dept: 10, role: 82, count: 1},
{name: "全部仍隔离租户", roleScope: "1", dept: 10, role: 82, count: 4},
{name: "部门及下级或本人", roleScope: "6", dept: 40, role: 82, count: 2},
{name: "本组织及下一级", roleScope: "7", dept: 10, role: 82, count: 2},
{name: "本组织下一级", roleScope: "8", dept: 10, role: 82, count: 1},
{name: "无下一级", roleScope: "8", dept: 40, role: 82, count: 0},
{name: "自定义空集合", roleScope: "2", dept: 10, role: 99, count: 0},
{name: "菜单收窄角色", roleScope: "1", menuScope: "5", dept: 10, role: 82, count: 1},
{name: "菜单放宽角色", roleScope: "5", menuScope: "1", dept: 10, role: 82, count: 4},
{name: "无角色", dept: 10, count: 0},
{name: "未知范围", roleScope: "unknown", dept: 10, role: 82, count: 0},
{name: "超管仍隔离租户", dept: 10, role: consts.SuperAdminRoleId, count: 4, super: true},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
roles := []model.Role{}
if tc.role > 0 {
roles = append(roles, model.Role{RoleId: tc.role, DataScope: tc.roleScope})
}
testCtx := contexts.Set(context.Background(), &model.Context{User: &model.Identity{BaseClaims: model.BaseClaims{ID: 7, DeptId: tc.dept, TenantId: "A", Roles: roles}}})
if tc.menuScope != "" {
contexts.SetDataValue(testCtx, "currentRoleId", "82")
contexts.SetDataValue(testCtx, "currentRoleDataScope", tc.menuScope)
}
list, total, err := List(testCtx, nil)
if err != nil || total != tc.count {
t.Fatalf("total=%d want=%d err=%v", total, tc.count, err)
}
for _, item := range list {
if _, err := Summary(testCtx, item.BatchNo); err != nil {
t.Fatal("列表与概览范围不一致", err)
}
}
if _, err := Summary(testCtx, "other-tenant"); err == nil {
t.Fatal("泄露其他租户")
}
if _, _, err := Details(testCtx, &model.AppExecutionLogDetailParam{BatchNo: "other-tenant"}); err == nil {
t.Fatal("泄露其他租户明细")
}
visibleOther := false
for _, item := range list {
if item.BatchNo == "other-user" {
visibleOther = true
}
}
rows, total, err := Details(testCtx, &model.AppExecutionLogDetailParam{BatchNo: "other-user"})
if visibleOther {
if err != nil || total != 1 || rows[0].ItemId != "other-card" {
t.Fatalf("可见部门明细缺失: %v %d %v", rows, total, err)
}
} else if err == nil {
t.Fatal("不可见批次明细被放行")
}
})
}
})
for _, sql := range []string{
`INSERT INTO batch_main(id,batch_no,batch_type,source_batch_no,batch_info,tenant_id,created_by) VALUES (4,'PARENT',3,'','{"jobRole":"allocate_parent"}','A',7),(5,'CHILD',3,'PARENT','{"jobRole":"allocate_chunk"}','A',7),(6,'FOREIGN-CHILD',3,'PARENT','{"jobRole":"allocate_chunk"}','A',8),(7,'RELATED',3,'PARENT','{}','A',7)`,
"INSERT INTO batch_detail(id,batch_no,row_no,item_id,detail_status,error_code,error_reason,tenant_id,created_by) VALUES(3,'PARENT',1,'parent-card',2,'','','A',7),(4,'CHILD',1,'mine-child',2,'','','A',7),(5,'FOREIGN-CHILD',1,'foreign-child',2,'','','A',8),(6,'RELATED',1,'related-child',2,'','','A',7)",
} {
if _, err := g.DB().Exec(ctx, sql); err != nil {
t.Fatal(err)
}
}
for _, sql := range []string{
`UPDATE batch_main SET target_dept_id=20,batch_info='{"jobRole":"allocate_parent","sourceDeptName":"提交时划拨单位","targetDeptName":"提交时接收单位"}' WHERE batch_no='PARENT'`,
`UPDATE batch_detail SET snapshot='{"sourceDeptId":10,"targetDeptId":20,"sourceDeptName":"明细划拨单位","targetDeptName":"明细接收单位"}' WHERE id=4`,
} {
if _, err := g.DB().Exec(ctx, sql); err != nil {
t.Fatal(err)
}
}
summary, err := Summary(ctx, "PARENT")
if err != nil || summary.SourceDeptName != "提交时划拨单位" || summary.TargetDeptName != "提交时接收单位" {
t.Fatal("概览未优先使用历史名称", summary, err)
}
details, total, err = Details(ctx, &model.AppExecutionLogDetailParam{BatchNo: "PARENT"})
if err != nil || total != 3 || len(details) != 3 {
t.Fatalf("child isolation: %+v %d %v", details, total, err)
}
seen := map[string]bool{}
for _, detail := range details {
if detail.ItemId == "mine-child" && (detail.SourceDeptName != "明细划拨单位" || detail.TargetDeptName != "明细接收单位") {
t.Fatal("明细快照名称被覆盖", detail)
}
if detail.ItemId != "mine-child" && detail.TargetDeptName != "提交时接收单位" {
t.Fatal("明细未继承批次历史接收单位", detail)
}
seen[detail.ItemId] = true
}
for _, itemID := range []string{"parent-card", "mine-child", "related-child"} {
if !seen[itemID] {
t.Fatalf("parent details missing %s: %+v", itemID, details)
}
}
list, total, err = List(ctx, nil)
if err != nil || total != 2 {
t.Fatalf("children appeared as submissions: %+v %d %v", list, total, err)
}
if _, err := AllocateSummary(ctx, "PARENT"); err != nil {
t.Fatal("allocate result rejected", err)
}
if _, total, err := AllocateDetails(ctx, &model.AppExecutionLogDetailParam{BatchNo: "PARENT"}); err != nil || total != 3 {
t.Fatal("allocate details rejected", total, err)
}
allCtx := contexts.Set(context.Background(), &model.Context{User: &model.Identity{BaseClaims: model.BaseClaims{ID: 8, DeptId: 20, TenantId: "A", Roles: []model.Role{{RoleId: 82, DataScope: consts.SysRoleDataScopeAll}}}}})
if _, err := Summary(allCtx, "PARENT"); err != nil {
t.Fatal("全部范围不能查看他人批次", err)
}
if _, err := AllocateSummary(allCtx, "PARENT"); err == nil {
t.Fatal("办理恢复接口泄露他人任务")
}
if _, _, err := AllocateDetails(allCtx, &model.AppExecutionLogDetailParam{BatchNo: "PARENT"}); err == nil {
t.Fatal("办理明细接口泄露他人任务")
}
for _, no := range []string{"mine", "CHILD", "RELATED", "other-user", "other-tenant", "missing"} {
if _, err := AllocateSummary(ctx, no); err == nil {
t.Fatalf("allocate summary exposed %s", no)
}
if _, _, err := AllocateDetails(ctx, &model.AppExecutionLogDetailParam{BatchNo: no}); err == nil {
t.Fatalf("allocate details exposed %s", no)
}
}
for _, no := range []string{"CHILD", "RELATED"} {
if _, err := Summary(ctx, no); err == nil {
t.Fatalf("child exposed as a standalone submission: %s", no)
}
}
if _, err := g.DB().Exec(ctx, "INSERT INTO batch_main(id,batch_no,batch_type,source_batch_no,tenant_id,created_by) VALUES (8,'SELF',3,'SELF','A',7)"); err != nil {
t.Fatal(err)
}
if _, err := g.DB().Exec(ctx, "INSERT INTO batch_detail(id,batch_no,row_no,item_id,detail_status,error_code,error_reason,tenant_id,created_by) VALUES(7,'SELF',1,'self-card',2,'','','A',7)"); err != nil {
t.Fatal(err)
}
if _, err := Summary(ctx, "SELF"); err != nil {
t.Fatal("self-referencing allocation hidden", err)
}
details, total, err = Details(ctx, &model.AppExecutionLogDetailParam{BatchNo: "SELF"})
if err != nil || total != 1 || len(details) != 1 || details[0].ItemId != "self-card" {
t.Fatalf("self allocation details lost: %+v %d %v", details, total, err)
}
}
func TestSafeReason(t *testing.T) {
for _, s := range []string{"token=abc", "dial tcp 192.0.2.10", "secret=abc", "panic\nstack", "https://internal"} {
if safeReason(s) == s {
t.Fatalf("sensitive reason exposed: %s", s)
}
}
if safeReason("套餐已过期") != "套餐已过期" {
t.Fatal("business explanation lost")
}
p, n := paging(0, 5000)
if p != 1 || n != 100 {
t.Fatal(p, n)
}
}
+75
View File
@@ -0,0 +1,75 @@
// ================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// You can delete these comments if you wish manually maintain this interface file.
// ================================================================================
package service
import (
"context"
"git.5iot.com/iotlcm-public/iotlcm-app/internal/library/xgorm/handler"
"git.5iot.com/iotlcm-public/iotlcm-app/internal/model/genin"
"git.5iot.com/iotlcm-public/iotlcm-app/utility/tree"
"github.com/gogf/gf/v2/database/gdb"
)
type (
IGenTestDemo interface {
// Model 测试单表ORM模型
Model(ctx context.Context, option ...*handler.Option) *gdb.Model
// List 获取测试单表列表
List(ctx context.Context, in *genin.TestDemoListParam) (list []*genin.TestDemoListModel, totalCount int, err error)
// Export 导出测试单表
Export(ctx context.Context, in *genin.TestDemoListParam) (err error)
// Edit 修改/新增测试单表
Edit(ctx context.Context, in *genin.TestDemoEditParam) (err error)
// Delete 删除测试单表
Delete(ctx context.Context, in *genin.TestDemoDeleteParam) (err error)
// View 获取测试单表指定信息
View(ctx context.Context, in *genin.TestDemoViewParam) (res *genin.TestDemoViewModel, err error)
}
IGenTestTree interface {
// Model 测试树表ORM模型
Model(ctx context.Context, option ...*handler.Option) *gdb.Model
// List 获取测试树表列表
List(ctx context.Context, in *genin.TestTreeListParam) (list []*genin.TestTreeListModel, totalCount int, err error)
// Export 导出测试树表
Export(ctx context.Context, in *genin.TestTreeListParam) (err error)
// Edit 修改/新增测试树表
Edit(ctx context.Context, in *genin.TestTreeEditParam) (err error)
// Delete 删除测试树表
Delete(ctx context.Context, in *genin.TestTreeDeleteParam) (err error)
// View 获取测试树表指定信息
View(ctx context.Context, in *genin.TestTreeViewParam) (res *genin.TestTreeViewModel, err error)
// TreeOption 获取测试树表关系树选项
TreeOption(ctx context.Context) (nodes []tree.Node, err error)
}
)
var (
localGenTestDemo IGenTestDemo
localGenTestTree IGenTestTree
)
func GenTestDemo() IGenTestDemo {
if localGenTestDemo == nil {
panic("implement not found for interface IGenTestDemo, forgot register?")
}
return localGenTestDemo
}
func RegisterGenTestDemo(i IGenTestDemo) {
localGenTestDemo = i
}
func GenTestTree() IGenTestTree {
if localGenTestTree == nil {
panic("implement not found for interface IGenTestTree, forgot register?")
}
return localGenTestTree
}
func RegisterGenTestTree(i IGenTestTree) {
localGenTestTree = i
}
+56
View File
@@ -0,0 +1,56 @@
// ================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// You can delete these comments if you wish manually maintain this interface file.
// ================================================================================
package service
import (
"context"
"git.5iot.com/iotlcm-public/iotlcm-app/internal/library/xgorm/handler"
"git.5iot.com/iotlcm-public/iotlcm-app/internal/model"
"github.com/gogf/gf/v2/database/gdb"
)
type (
ISysGenTable interface {
Model(ctx context.Context, option ...*handler.Option) *gdb.Model
// 列表
List(ctx context.Context, param *model.SysGenTableListParam) (output []*model.SysGenTableListModel, total int, err error)
// 详情
View(ctx context.Context, param *model.SysGenTableViewParam) (output *model.SysGenTableViewModel, err error)
// 新增
Add(ctx context.Context, param *model.SysGenTableAddParam) (output *model.SysGenTableAddModel, err error)
// 修改
Edit(ctx context.Context, param *model.SysGenTableEditParam) (output *model.SysGenTableEditModel, err error)
Delete(ctx context.Context, param *model.SysGenTableDeleteParam) (output *model.SysGenTableDeleteModel, err error)
// 获取选择项
Selects(ctx context.Context) (output *model.SelectsModel, err error)
// 获取表选择项
TableSelect(ctx context.Context, param *model.GenCodesTableSelectParam) (output []*model.GenCodesTableSelectModel, err error)
// 获取字段选择项
ColumnSelect(ctx context.Context, param *model.GenCodesColumnSelectParam) (output []*model.GenCodesColumnSelectModel, err error)
// 获取字段列表
ColumnList(ctx context.Context, param *model.GenCodesColumnListParam) (output []*model.GenCodesColumnListModel, err error)
// 预览
Preview(ctx context.Context, param *model.GenCodesPreviewParam) (output *model.GenCodesPreviewModel, err error)
// 构建
Build(ctx context.Context, param *model.GenCodesBuildParam) (output *model.GenCodesBuildModel, err error)
}
)
var (
localSysGenTable ISysGenTable
)
func SysGenTable() ISysGenTable {
if localSysGenTable == nil {
panic("implement not found for interface ISysGenTable, forgot register?")
}
return localSysGenTable
}
func RegisterSysGenTable(i ISysGenTable) {
localSysGenTable = i
}
+38
View File
@@ -0,0 +1,38 @@
// ================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// You can delete these comments if you wish manually maintain this interface file.
// ================================================================================
package service
import (
"git.5iot.com/iotlcm-public/iotlcm-app/internal/model"
)
type (
IInfo interface {
GetInfo() *model.InfoModel
SetInfoName(Type string, Name string, Title string, Description string)
SetInfoBuild(buildVersion string, buildTime string, commitID string)
SetInfoData(data map[string]interface{})
GetInfoData() map[string]interface{}
AddInfoData(key string, value interface{})
DelInfoData(key string)
GetInfoDataByKey(key string) interface{}
}
)
var (
localInfo IInfo
)
func Info() IInfo {
if localInfo == nil {
panic("implement not found for interface IInfo, forgot register?")
}
return localInfo
}
func RegisterInfo(i IInfo) {
localInfo = i
}
+198
View File
@@ -0,0 +1,198 @@
// ================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// You can delete these comments if you wish manually maintain this interface file.
// ================================================================================
package service
import (
"context"
"git.5iot.com/iotlcm-public/iotlcm-app/internal/model"
"github.com/gogf/gf/v2/database/gdb"
)
type (
IMemoryDB interface {
Init(ctx context.Context) error
EventHandler(ctx context.Context, chg *model.BroadcastDbChgCache) error
DB(ctx context.Context) gdb.DB
Ctx(ctx context.Context) gdb.DB
}
IMemDBSysConfig interface {
Init(ctx context.Context) error
EventHandler(ctx context.Context) error
TableName(ctx context.Context) string
CreateTable(ctx context.Context) error
LoadData(ctx context.Context, pk int64) error
DeleteData(ctx context.Context, ids []int64) error
}
IMemDBSysDept interface {
Init(ctx context.Context) error
EventHandler(ctx context.Context) error
TableName(ctx context.Context) string
CreateTable(ctx context.Context) error
LoadData(ctx context.Context, pk int64) error
DeleteData(ctx context.Context, ids []int64) error
}
IMemDBSysDictData interface {
Init(ctx context.Context) error
EventHandler(ctx context.Context) error
TableName(ctx context.Context) string
CreateTable(ctx context.Context) error
LoadData(ctx context.Context, pk int64) error
DeleteData(ctx context.Context, ids []int64) error
}
IMemDBSysDictType interface {
Init(ctx context.Context) error
EventHandler(ctx context.Context) error
TableName(ctx context.Context) string
CreateTable(ctx context.Context) error
LoadData(ctx context.Context, pk int64) error
DeleteData(ctx context.Context, ids []int64) error
}
IMemDBSysMenu interface {
Init(ctx context.Context) error
EventHandler(ctx context.Context) error
TableName(ctx context.Context) string
CreateTable(ctx context.Context) error
LoadData(ctx context.Context, pk int64) error
DeleteData(ctx context.Context, ids []int64) error
}
IMemDBSysPost interface {
Init(ctx context.Context) error
EventHandler(ctx context.Context) error
TableName(ctx context.Context) string
CreateTable(ctx context.Context) error
LoadData(ctx context.Context, pk int64) error
DeleteData(ctx context.Context, ids []int64) error
}
IMemDBSysRole interface {
Init(ctx context.Context) error
EventHandler(ctx context.Context) error
TableName(ctx context.Context) string
CreateTable(ctx context.Context) error
LoadData(ctx context.Context, pk int64) error
DeleteData(ctx context.Context, ids []int64) error
}
IMemDBSysTenant interface {
Init(ctx context.Context) error
EventHandler(ctx context.Context) error
DB(ctx context.Context) gdb.DB
TableName(ctx context.Context) string
CreateTable(ctx context.Context) error
LoadData(ctx context.Context, pk int64) error
DeleteData(ctx context.Context, ids []int64) error
}
)
var (
localMemoryDB IMemoryDB
localMemDBSysConfig IMemDBSysConfig
localMemDBSysDept IMemDBSysDept
localMemDBSysDictData IMemDBSysDictData
localMemDBSysDictType IMemDBSysDictType
localMemDBSysMenu IMemDBSysMenu
localMemDBSysPost IMemDBSysPost
localMemDBSysRole IMemDBSysRole
localMemDBSysTenant IMemDBSysTenant
)
func MemoryDB() IMemoryDB {
if localMemoryDB == nil {
panic("implement not found for interface IMemoryDB, forgot register?")
}
return localMemoryDB
}
func RegisterMemoryDB(i IMemoryDB) {
localMemoryDB = i
}
func MemDBSysConfig() IMemDBSysConfig {
if localMemDBSysConfig == nil {
panic("implement not found for interface IMemDBSysConfig, forgot register?")
}
return localMemDBSysConfig
}
func RegisterMemDBSysConfig(i IMemDBSysConfig) {
localMemDBSysConfig = i
}
func MemDBSysDept() IMemDBSysDept {
if localMemDBSysDept == nil {
panic("implement not found for interface IMemDBSysDept, forgot register?")
}
return localMemDBSysDept
}
func RegisterMemDBSysDept(i IMemDBSysDept) {
localMemDBSysDept = i
}
func MemDBSysDictData() IMemDBSysDictData {
if localMemDBSysDictData == nil {
panic("implement not found for interface IMemDBSysDictData, forgot register?")
}
return localMemDBSysDictData
}
func RegisterMemDBSysDictData(i IMemDBSysDictData) {
localMemDBSysDictData = i
}
func MemDBSysDictType() IMemDBSysDictType {
if localMemDBSysDictType == nil {
panic("implement not found for interface IMemDBSysDictType, forgot register?")
}
return localMemDBSysDictType
}
func RegisterMemDBSysDictType(i IMemDBSysDictType) {
localMemDBSysDictType = i
}
func MemDBSysMenu() IMemDBSysMenu {
if localMemDBSysMenu == nil {
panic("implement not found for interface IMemDBSysMenu, forgot register?")
}
return localMemDBSysMenu
}
func RegisterMemDBSysMenu(i IMemDBSysMenu) {
localMemDBSysMenu = i
}
func MemDBSysPost() IMemDBSysPost {
if localMemDBSysPost == nil {
panic("implement not found for interface IMemDBSysPost, forgot register?")
}
return localMemDBSysPost
}
func RegisterMemDBSysPost(i IMemDBSysPost) {
localMemDBSysPost = i
}
func MemDBSysRole() IMemDBSysRole {
if localMemDBSysRole == nil {
panic("implement not found for interface IMemDBSysRole, forgot register?")
}
return localMemDBSysRole
}
func RegisterMemDBSysRole(i IMemDBSysRole) {
localMemDBSysRole = i
}
func MemDBSysTenant() IMemDBSysTenant {
if localMemDBSysTenant == nil {
panic("implement not found for interface IMemDBSysTenant, forgot register?")
}
return localMemDBSysTenant
}
func RegisterMemDBSysTenant(i IMemDBSysTenant) {
localMemDBSysTenant = i
}
+46
View File
@@ -0,0 +1,46 @@
// ================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// You can delete these comments if you wish manually maintain this interface file.
// ================================================================================
package service
import (
"context"
"github.com/gogf/gf/v2/net/ghttp"
)
type (
IMiddleware interface {
// 响应处理中间件
ResponseHandler(r *ghttp.Request)
// Ctx 自定义上下文对象
Ctx(r *ghttp.Request)
// CORS 跨域处理
CORS(r *ghttp.Request)
// Auth 认证处理
Auth(r *ghttp.Request)
// IsExceptLogin 判断是否需要验证登录
IsExceptLogin(ctx context.Context, path string) bool
// OperationLog 操作日志
OperationLog(r *ghttp.Request)
// ResponseHandlerRaw 返回处理中间件
ResponseHandlerRaw(r *ghttp.Request)
}
)
var (
localMiddleware IMiddleware
)
func Middleware() IMiddleware {
if localMiddleware == nil {
panic("implement not found for interface IMiddleware, forgot register?")
}
return localMiddleware
}
func RegisterMiddleware(i IMiddleware) {
localMiddleware = i
}
+44
View File
@@ -0,0 +1,44 @@
// ================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// You can delete these comments if you wish manually maintain this interface file.
// ================================================================================
package service
import (
"context"
"git.5iot.com/iotlcm-public/iotlcm-app/internal/model"
)
type (
IMonitorServer interface {
GetGoInfo(ctx context.Context) (res *model.GoRunInfo)
// GetHostInfo 获取主机信息
GetHostInfo(ctx context.Context) (data []byte)
// GetSysLoad 获取系统负载信息
GetSysLoad(ctx context.Context) (data []byte)
// GetCpuInfo 获取CPU信息
GetCpuInfo(ctx context.Context) (data []byte)
// GetMemInfo 获取内存信息
GetMemInfo(ctx context.Context) (data []byte)
// GetDiskInfo 获取磁盘信息
GetDiskInfo(ctx context.Context) (data []byte)
// GetNetStatusInfo 获取网络信息
GetNetStatusInfo(ctx context.Context) (data []byte)
}
)
var (
localMonitorServer IMonitorServer
)
func MonitorServer() IMonitorServer {
if localMonitorServer == nil {
panic("implement not found for interface IMonitorServer, forgot register?")
}
return localMonitorServer
}
func RegisterMonitorServer(i IMonitorServer) {
localMonitorServer = i
}
+612
View File
@@ -0,0 +1,612 @@
// ================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// You can delete these comments if you wish manually maintain this interface file.
// ================================================================================
package service
import (
"context"
v1 "git.5iot.com/iotlcm-public/iotlcm-app/api/system/v1"
"git.5iot.com/iotlcm-public/iotlcm-app/internal/library/xgorm/handler"
"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/internal/model/request"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/net/ghttp"
)
type (
ISysAuth interface {
Login(ctx context.Context, param *model.LoginParams) (res *model.LoginUserOut, token string, err error)
// 生成Token
GenerateToken(ctx context.Context, user *model.LoginUserOut) (claims *model.CustomClaims, token string, err error)
// 解析Token
ParseToken(ctx context.Context, token string) (claims *model.CustomClaims, err error)
// 删除Token
DeleteToken(ctx context.Context, token string) (err error)
// 获取token
GetAccessToken(ctx context.Context) (token string, err error)
// 根据Token获取当前登录用户信息
GetCurrentUser(ctx context.Context) (claims *model.CustomClaims, err error)
// 获取用户权限码
GetUserAccessCodeList(ctx context.Context, userId int64) (accessCodeList []string, menuRoleAccessCodeList []string, err error)
// 根据openId登录
LoginByOpenId(ctx context.Context, social *model.SysSocialListModel) (res *model.LoginUserOut, token string, err error)
}
ISysCaptcha interface {
// 生成验证码
GenerateCaptcha(ctx context.Context) (key string, image string, err error)
// 验证验证码
VerifyCaptcha(ctx context.Context, key string, value string) (err error)
}
ISysClient interface {
List(ctx context.Context, query *model.SysClientListParam) (items []*model.SysClientListModel, total int, err error)
View(ctx context.Context, param *model.SysClientViewParam) (item *model.SysClientViewModel, err error)
Add(ctx context.Context, param *model.SysClientAddParam) (output *model.SysClientAddModel, err error)
Edit(ctx context.Context, param *model.SysClientEditParam) (output *model.SysClientEditModel, err error)
Delete(ctx context.Context, param *model.SysClientDeleteParam) (output *model.SysClientDeleteModel, err error)
Status(ctx context.Context, param *model.SysClientStatusParam) (output *model.SysClientStatusModel, err error)
}
ISysConfig interface {
Model(ctx context.Context, option ...*handler.Option) *gdb.Model
ModelQuery(ctx context.Context, option ...*handler.Option) *gdb.Model
List(ctx context.Context, param *model.SysConfigListParam) (items []*model.SysConfigListModel, total int, err error)
Add(ctx context.Context, param *model.SysConfigAddParam) (output *model.SysConfigAddModel, err error)
Edit(ctx context.Context, param *model.SysConfigEditParam) (output *model.SysConfigEditModel, err error)
Delete(ctx context.Context, param *model.SysConfigDeleteParam) (output *model.SysConfigDeleteModel, err error)
View(ctx context.Context, param *model.SysConfigViewParam) (output *model.SysConfigViewModel, err error)
GetConfigByKey(ctx context.Context, configKey string) (config *entity.SysConfig, err error)
}
ISysDept interface {
Model(ctx context.Context, option ...*handler.Option) *gdb.Model
ModelQuery(ctx context.Context, option ...*handler.Option) *gdb.Model
GetDeptList(ctx context.Context, query model.SysDeptListParam) (items []*model.SysDeptListModel, total int, err error)
GetDeptById(ctx context.Context, id int64) (dept *model.SysDeptViewModel, err error)
// 构建树结构
DeptTree(ctx context.Context, parentDept *model.SysDeptTreeModel, deptList []*model.SysDeptListModel, ancestors string) (data []*model.SysDeptTreeModel, err error)
GetDeptTree(ctx context.Context, query model.SysDeptTreeParam) (items []*model.SysDeptTreeModel, err error)
// 递归构建结构
RecursionDeptIds(ctx context.Context, parentId int64, deptList []*model.SysDeptListModel, data *[]int64) (err error)
// 根据父部门id获取部门列表
GetDeptIdsByParentId(ctx context.Context, parentId int64) (ids []int64, err error)
AddDept(ctx context.Context, dept *model.SysDeptAddModel) (deptId int64, err error)
EditDept(ctx context.Context, dept *model.SysDeptEditModel) (deptId int64, err error)
DeleteDept(ctx context.Context, dept *model.SysDeptDeleteModel) (deptId int64, err error)
// 刷新部门 ancestors
RefreshDeptAncestors(ctx context.Context) (err error)
GetParentIDAncestors(ctx context.Context, depts []*model.SysDeptViewModel, printId int64, ancestors *string) (err error)
GetDeptListByIds(ctx context.Context, ids []int64) (depts []*model.SysDeptListModel, err error)
// 验证上级是否是公司
ValidateParentIsCompany(ctx context.Context, parentId int64) (isCompany bool, err error)
// 获取组织公司Map
GetDeptCompanyMap(ctx context.Context) (map[int64]string, error)
// 获取上级公司组织信息
GetParentCompanyInfo(ctx context.Context, deptId int64) *model.SysDeptListModel
}
ISysDictData interface {
Model(ctx context.Context, option ...*handler.Option) *gdb.Model
ModelQuery(ctx context.Context, option ...*handler.Option) *gdb.Model
List(ctx context.Context, param *model.SysDictDataListParam) (items []model.SysDictDataListModel, total int, err error)
View(ctx context.Context, param *model.SysDictDataViewParam) (data *model.SysDictDataViewModel, err error)
Add(ctx context.Context, param *model.SysDictDataAddParam) (output *model.SysDictDataAddModel, err error)
Edit(ctx context.Context, param *model.SysDictDataEditParam) (output *model.SysDictDataEditModel, err error)
Delete(ctx context.Context, param *model.SysDictDataDeleteParam) (output *model.SysDictDataDeleteModel, err error)
GetDictLabel(ctx context.Context, dictType string, dictCode string) string
GetDictListByTypes(ctx context.Context, dictTypes []string) (dictDataList []model.SysDictDataListModel, err error)
}
ISysDictType interface {
Model(ctx context.Context, option ...*handler.Option) *gdb.Model
ModelQuery(ctx context.Context, option ...*handler.Option) *gdb.Model
List(ctx context.Context, param *model.SysDictTypeListParam) (items []model.SysDictTypeListModel, total int, err error)
View(ctx context.Context, param *model.SysDictTypeViewParam) (data *model.SysDictTypeViewModel, err error)
Add(ctx context.Context, param *model.SysDictTypeAddParam) (output *model.SysDictTypeAddModel, err error)
Edit(ctx context.Context, param *model.SysDictTypeEditParam) (output *model.SysDictTypeEditModel, err error)
Delete(ctx context.Context, param *model.SysDictTypeDeleteParam) (output *model.SysDictTypeDeleteModel, err error)
}
ISysJob interface {
Model(ctx context.Context, option ...*handler.Option) *gdb.Model
List(ctx context.Context, query *model.SysJobListParam, pageInfo *request.PageInfo) (Data []*model.SysJobListModel, total int, err error)
View(ctx context.Context, jobId int64) (Data *model.SysJobViewModel, err error)
Add(ctx context.Context, jobAdd *model.SysJobAddModel) (LastInsertId int64, err error)
Update(ctx context.Context, jobUpdate *model.SysJobUpdateModel) (RowsAffected int64, err error)
UpdateStatus(ctx context.Context, jobUpdate *model.SysJobUpdateStatusModel) (RowsAffected int64, err error)
Delete(ctx context.Context, jobDelete *model.SysJobDeleteModel) (RowsAffected int64, err error)
Exec(ctx context.Context, jobId int64) error
// 查询所有的状态正常的任务列表,并进行初始注册
InitRegister() error
}
ISysLogininfor interface {
Model(ctx context.Context, option ...*handler.Option) *gdb.Model
List(ctx context.Context, param *model.SysLogininforListParam) (items []*model.SysLogininforListModel, total int, err error)
AddLogininfor(ctx context.Context, logininfor *model.SysLogininforAddModel) (id int64, err error)
Delete(ctx context.Context, param *model.SysLogininforDeleteParam) (output *model.SysLogininforDeleteModel, err error)
}
ISysMenu interface {
Model(ctx context.Context, option ...*handler.Option) *gdb.Model
ModelQuery(ctx context.Context, option ...*handler.Option) *gdb.Model
List(ctx context.Context, param *model.SysMenuListParam) (data []*model.SysMenuListModel, total int, err error)
// 获取租户菜单列表, 系统租户返回所有菜单,其他租户返回当前租户菜单
GetTenantMenu(ctx context.Context, query *model.SysMenuListParam) (data []*model.SysMenuListModel, total int, err error)
// 构建树结构
MenuTree(ctx context.Context, parentMenu *model.SysMenuTree, menuList []*entity.SysMenu) (data []*model.SysMenuTree, err error)
// 获取用户动态路由列表
GetUserMenu(ctx context.Context) (data []*entity.SysMenu, err error)
// 构建用户动态路由树
BuildUserMenuTree(ctx context.Context, parentMenu *v1.RouteMenu, menuList []*entity.SysMenu, allPath string) (data v1.MenuAllRes, err error)
// 获取用户动态路由树
GetUserMenuTree(ctx context.Context) (data v1.MenuAllRes, err error)
GetSysMenuView(ctx context.Context, menuId int64) (data *model.SysMenuViewModel, err error)
UpdateSysMenu(ctx context.Context, menu *model.SysMenuUpdateModel) (data *model.SysMenuViewModel, err error)
AddSysMenu(ctx context.Context, menu *model.SysMenuAddModel) (data *model.SysMenuViewModel, err error)
DeleteSysMenu(ctx context.Context, menuId int64) (err error)
GetFastList(ctx context.Context) (res map[int64]*entity.SysMenu, err error)
}
ISysNotice interface {
Model(ctx context.Context, option ...*handler.Option) *gdb.Model
List(ctx context.Context, param *model.SysNoticeListParam) (items []*model.SysNoticeListModel, total int, err error)
Add(ctx context.Context, param *model.SysNoticeAddParam) (err error)
Edit(ctx context.Context, param *model.SysNoticeEditParam) (err error)
Delete(ctx context.Context, param *model.SysNoticeDeleteParam) (err error)
View(ctx context.Context, param *model.SysNoticeViewParam) (data *model.SysNoticeViewModel, err error)
}
ISysNoticeUser interface {
// Model 用户通知公告表ORM模型
Model(ctx context.Context, option ...*handler.Option) *gdb.Model
// List 获取用户通知公告表列表
List(ctx context.Context, in *model.SysNoticeUserListParam) (list []*model.SysNoticeUserListModel, totalCount int, err error)
// Export 导出用户通知公告表
Export(ctx context.Context, in *model.SysNoticeUserListParam) (err error)
// Edit 修改/新增用户通知公告表
Edit(ctx context.Context, in *model.SysNoticeUserEditParam) (err error)
// Delete 删除用户通知公告表
Delete(ctx context.Context, in *model.SysNoticeUserDeleteParam) (err error)
// View 获取用户通知公告表指定信息
View(ctx context.Context, in *model.SysNoticeUserViewParam) (res *model.SysNoticeUserViewModel, err error)
// Status 更新用户通知公告表状态
Status(ctx context.Context, in *model.SysNoticeUserStatusParam) (err error)
// Read 已读
Read(ctx context.Context, in *model.SysNoticeUserReadParam) (err error)
}
ISysOperLog interface {
GetOperLogList(ctx context.Context, query *model.SysOperLogListParam, page *request.PageInfo) (items []*model.SysOperLogListModel, total int, err error)
AnalysisLog(ctx context.Context) (data *model.SysOperLogAddParam, err error)
ClearOperationLogByDays(ctx context.Context, days int) error
RealWrite(ctx context.Context, data model.SysOperLogAddParam) (err error)
}
ISysOss interface {
Model(ctx context.Context, option ...*handler.Option) *gdb.Model
List(ctx context.Context, param *model.SysOssListParam, pageInfo *request.PageInfo) (items []*model.SysOssListModel, total int, err error)
View(ctx context.Context, param *model.SysOssViewParam) (output *model.SysOssViewModel, err error)
Download(ctx context.Context, param *model.SysOssDownloadParam) (output *model.SysOssDownloadModel, err error)
Delete(ctx context.Context, param *model.SysOssDeleteParam) (output *model.SysOssDeleteModel, err error)
Upload(ctx context.Context, param *model.SysOssUploadParam) (output *model.SysOssUploadModel, err error)
MoveFile(ctx context.Context, param *model.SysOssMoveFileParam) (output *model.SysOssMoveFileModel, err error)
SaveContent(ctx context.Context, param *model.SysOssSaveContentParam) (output *model.SysOssSaveContentModel, err error)
GetSaveFilePathConfig(ctx context.Context, newFileType int, notAddDate int) (string, error)
MoveFileLocal(ctx context.Context, filePath string, newFileType int, useOriginalName int, subDirName string, isAddDate int) (result model.UploadResponse, err error)
// 保存内容
SaveContentLocal(ctx context.Context, content []byte, newFileType int, fileName string, subDirName string, isAddDate int) (result model.UploadResponse, err error)
UploadLocal(ctx context.Context, file *ghttp.UploadFile, newFileType int, useOriginalName int, subDirName string, notAddDate int) (result model.UploadResponse, err error)
DownloadLocal(ctx context.Context, file *entity.SysOss) (err error)
DeleteLocal(ctx context.Context, file *entity.SysOss) (err error)
CheckType(ctx context.Context, checkFileType string, file *ghttp.UploadFile) (err error)
CheckSize(ctx context.Context, checkFileType string, file *ghttp.UploadFile) (err error)
GetAllUrl(ctx context.Context, url string) (allUrl string, ossId int64, fileSize int64, OriginalName string, md5 string, hmac string, err error)
}
ISysOssConfig interface {
Model(ctx context.Context, option ...*handler.Option) *gdb.Model
List(ctx context.Context, param *model.SysOssConfigListParam) (items []*model.SysOssConfigListModel, total int, err error)
View(ctx context.Context, param *model.SysOssConfigViewParam) (item *model.SysOssConfigViewModel, err error)
Add(ctx context.Context, param *model.SysOssConfigAddParam) (item *model.SysOssConfigAddModel, err error)
Edit(ctx context.Context, param *model.SysOssConfigEditParam) (item *model.SysOssConfigEditModel, err error)
Delete(ctx context.Context, param *model.SysOssConfigDeleteParam) (item *model.SysOssConfigDeleteModel, err error)
GetAllUrlByService(ctx context.Context, path string, tenantId string, ossService string) (allUrl string, err error)
}
ISysPost interface {
Model(ctx context.Context, option ...*handler.Option) *gdb.Model
ModelQuery(ctx context.Context, option ...*handler.Option) *gdb.Model
List(ctx context.Context, query *model.SysPostListParam) (items []*model.SysPostListModel, total int, err error)
View(ctx context.Context, param *model.SysPostViewParam) (post *model.SysPostViewModel, err error)
Add(ctx context.Context, param *model.SysPostAddParam) (post *model.SysPostAddModel, err error)
Edit(ctx context.Context, param *model.SysPostEditParam) (post *model.SysPostEditModel, err error)
Delete(ctx context.Context, param *model.SysPostDeleteParam) (post *model.SysPostDeleteModel, err error)
Export(ctx context.Context, param *model.SysPostExportParam) (post *model.SysPostExportModel, err error)
}
ISysQrcode interface {
// 获取登录二维码
GetQrcodeLogin(ctx context.Context) (res *model.QrcodeLoginModel, err error)
// 获取登录二维码扫码结果
GetQrcodeLoginStatus(ctx context.Context, tempUserId string) (res *model.QrcodeLoginModel, err error)
// 获取绑定二维码
GetQrcodeBind(ctx context.Context) (res *model.QrcodeCacheModel, err error)
// 获取绑定二维码扫码结果
GetQrcodeBindStatus(ctx context.Context) (res *model.QrcodeBindStatusModel, err error)
// 扫码回调
QrcodeScanCallback(ctx context.Context, param *model.QrcodeScanCallbackParam) (err error)
// 扫码后选择用户登录
QrcodeLoginSelectUserId(ctx context.Context, param *model.QrcodeLoginSelectUserIdParam) (res *model.QrcodeLoginAndBindOpenIdModel, err error)
// 登录并绑定
QrcodeLoginAndBindOpenId(ctx context.Context, param *model.QrcodeLoginAndBindOpenIdParam) (res *model.QrcodeLoginAndBindOpenIdModel, err error)
// 注册并绑定
QrcodeRegisterAndBindOpenId(ctx context.Context, param *model.QrcodeRegisterAndBindOpenIdParam) (res *model.QrcodeLoginAndBindOpenIdModel, err error)
}
ISysRole interface {
Model(ctx context.Context, option ...*handler.Option) *gdb.Model
ModelQuery(ctx context.Context, option ...*handler.Option) *gdb.Model
// 获取租户下角色列表
List(ctx context.Context, param *model.SysRoleListParam) (res []*model.SysRoleListModel, total int, err error)
// 获取角色详情
View(ctx context.Context, param *model.SysRoleViewParam) (res *model.SysRoleViewModel, err error)
// 获取角色菜单
GetRoleMenu(ctx context.Context, id int64) (res []*entity.SysMenu, err error)
// 获取角色部门
GetRoleDept(ctx context.Context, id int64) (res []*entity.SysDept, err error)
// 获取角色列表对应菜单
GetRoleListMenu(ctx context.Context, ids []int64) (res []*entity.SysRoleMenu, err error)
// 新增角色
Add(ctx context.Context, param *model.SysRoleAddParam) (role *model.SysRoleAddModel, err error)
// 编辑角色
Edit(ctx context.Context, param *model.SysRoleEditParam) (role *model.SysRoleEditModel, err error)
// 删除角色
Delete(ctx context.Context, param *model.SysRoleDeleteParam) (role *model.SysRoleDeleteModel, err error)
RoleMenu(ctx context.Context, roleId int64, menuIds []int64) (err error)
// 自定义角色部门数据权限
RoleDept(ctx context.Context, roleId int64, deptIds []int64) (err error)
// 编辑角色数据权限
EditRoleDataScope(ctx context.Context, model *model.SysRoleDataScopeEditParam) (err error)
}
ISysSocial interface {
// 查询社会化关系表
List(ctx context.Context, query *model.SysSocialListParam, page *request.PageInfo) (items []*model.SysSocialListModel, total int, err error)
// 删除社会化关系表
Delete(ctx context.Context, id int64) (err error)
Create(ctx context.Context, social *model.SysSocialSaveParam) (err error)
}
ISysTenant interface {
ModelQuery(ctx context.Context, option ...*handler.Option) *gdb.Model
// 获取租户信息
View(ctx context.Context, param *model.SysTenantViewParam) (data *model.SysTenantViewModel, err error)
// 获取租户列表
List(ctx context.Context, param *model.SysTenantListParam) (data []*model.SysTenantListModel, total int, err error)
Add(ctx context.Context, param *model.SysTenantAddParam) (output *model.SysTenantAddModel, err error)
Edit(ctx context.Context, param *model.SysTenantEditParam) (output *model.SysTenantEditModel, err error)
Delete(ctx context.Context, param *model.SysTenantDeleteParam) (output *model.SysTenantDeleteModel, err error)
Status(ctx context.Context, param *model.SysTenantStatusParam) (output *model.SysTenantStatusModel, err error)
// 新建租户插入系统参数表数据
BatchInsertSysParam(ctx context.Context, tx gdb.TX, tenantId string, createdBy int64, createdDept int64) (ids []int64, err error)
// 同步租户菜单
SyncTenantMenu(ctx context.Context, tenantId string) (err error)
// 查询用户ID是否是租户管理员
IsTenantAdmin(ctx context.Context, userIds []int64) (isAdminUserIds []int64, err error)
}
ISysTenantPackage interface {
// 获取租户套餐
View(ctx context.Context, param *model.SysTenantPackageViewParam) (data *model.SysTenantPackageViewModel, err error)
List(ctx context.Context, param *model.SysTenantPackageListParam) (data []*model.SysTenantPackageListModel, total int, err error)
Add(ctx context.Context, param *model.SysTenantPackageAddParam) (output *model.SysTenantPackageAddModel, err error)
Edit(ctx context.Context, param *model.SysTenantPackageEditParam) (output *model.SysTenantPackageEditModel, err error)
Delete(ctx context.Context, param *model.SysTenantPackageDeleteParam) (output *model.SysTenantPackageDeleteModel, err error)
Status(ctx context.Context, param *model.SysTenantPackageStatusParam) (output *model.SysTenantPackageStatusModel, err error)
}
ISysUser interface {
Model(ctx context.Context, option ...*handler.Option) *gdb.Model
// 根据用户名获取用户信息,不验证当前租户
GetUserByUsername(ctx context.Context, username string, tenantId string) (user *entity.SysUser, err error)
// 根据邮箱获取用户信息,不验证当前租户
GetUserByEmail(ctx context.Context, email string, tenantId string) (user *entity.SysUser, err error)
// 根据手机号获取用户信息,不验证当前租户
GetUserByPhone(ctx context.Context, phone string, tenantId string) (user *entity.SysUser, err error)
// 根据用户名和密码获取用户信息
GetUserByUsernameAndPassword(ctx context.Context, tenantId string, username string, password string) (user *entity.SysUser, err error)
// 根据用户ID获取用户信息
GetUserById(ctx context.Context, id int64) (user *model.SysUserViewModel, err error)
// 获取用户列表
List(ctx context.Context, page *request.PageInfo, query *model.UserListParam) (items []*model.SysUserListModel, total int, err error)
// 新增用户
AddUser(ctx context.Context, req *model.SysUserAddModel) (data *model.SysUserViewModel, err error)
Profile(ctx context.Context) (user *model.UserProfileModel, err error)
UpdateCurrentUser(ctx context.Context, req *model.UpdateCurrentUserModel) (user *model.SysUserViewModel, err error)
UpdateCurrentUserPassword(ctx context.Context, req *model.UpdateCurrentUserPasswordModel) (err error)
UpdateUser(ctx context.Context, req *model.SysUserUpdateModel) (err error)
DeleteUser(ctx context.Context, userIds []int64) (err error)
ResetPassword(ctx context.Context, userId int64, password string) (err error)
UpdateLoginInfo(ctx context.Context, userId int64, loginIp string) (err error)
// 获取用户角色ID列表
GetUserRoleIds(ctx context.Context, userId int64) (roleIds []int64, err error)
// 获取用户岗位ID列表
GetUserPostIds(ctx context.Context, userId int64) (postIds []int64, err error)
Register(ctx context.Context, param *model.SysUserRegisterModel) (err error)
// Status 更新用户状态
Status(ctx context.Context, param *model.SysUserStatusParam) (err error)
// 批量查询用户迷你信息
BatchGetUserMiniInfo(ctx context.Context, userIds []int64) (users []*model.SysUserMiniModel, err error)
}
ISysUserOnline interface {
Model(ctx context.Context, option ...*handler.Option) *gdb.Model
Add(ctx context.Context, userOnline *model.SysUserOnlineAddModel) (err error)
List(ctx context.Context, query *model.SysUserOnlineListParam, page *request.PageInfo) (items []*model.SysUserOnlineListModel, total int, err error)
Delete(ctx context.Context, ids []int64) (err error)
DeleteByToken(ctx context.Context, token string) (err error)
}
)
var (
localSysAuth ISysAuth
localSysCaptcha ISysCaptcha
localSysClient ISysClient
localSysConfig ISysConfig
localSysDept ISysDept
localSysDictData ISysDictData
localSysDictType ISysDictType
localSysJob ISysJob
localSysLogininfor ISysLogininfor
localSysMenu ISysMenu
localSysNotice ISysNotice
localSysNoticeUser ISysNoticeUser
localSysOperLog ISysOperLog
localSysOss ISysOss
localSysOssConfig ISysOssConfig
localSysPost ISysPost
localSysQrcode ISysQrcode
localSysRole ISysRole
localSysSocial ISysSocial
localSysTenant ISysTenant
localSysTenantPackage ISysTenantPackage
localSysUser ISysUser
localSysUserOnline ISysUserOnline
)
func SysAuth() ISysAuth {
if localSysAuth == nil {
panic("implement not found for interface ISysAuth, forgot register?")
}
return localSysAuth
}
func RegisterSysAuth(i ISysAuth) {
localSysAuth = i
}
func SysCaptcha() ISysCaptcha {
if localSysCaptcha == nil {
panic("implement not found for interface ISysCaptcha, forgot register?")
}
return localSysCaptcha
}
func RegisterSysCaptcha(i ISysCaptcha) {
localSysCaptcha = i
}
func SysClient() ISysClient {
if localSysClient == nil {
panic("implement not found for interface ISysClient, forgot register?")
}
return localSysClient
}
func RegisterSysClient(i ISysClient) {
localSysClient = i
}
func SysConfig() ISysConfig {
if localSysConfig == nil {
panic("implement not found for interface ISysConfig, forgot register?")
}
return localSysConfig
}
func RegisterSysConfig(i ISysConfig) {
localSysConfig = i
}
func SysDept() ISysDept {
if localSysDept == nil {
panic("implement not found for interface ISysDept, forgot register?")
}
return localSysDept
}
func RegisterSysDept(i ISysDept) {
localSysDept = i
}
func SysDictData() ISysDictData {
if localSysDictData == nil {
panic("implement not found for interface ISysDictData, forgot register?")
}
return localSysDictData
}
func RegisterSysDictData(i ISysDictData) {
localSysDictData = i
}
func SysDictType() ISysDictType {
if localSysDictType == nil {
panic("implement not found for interface ISysDictType, forgot register?")
}
return localSysDictType
}
func RegisterSysDictType(i ISysDictType) {
localSysDictType = i
}
func SysJob() ISysJob {
if localSysJob == nil {
panic("implement not found for interface ISysJob, forgot register?")
}
return localSysJob
}
func RegisterSysJob(i ISysJob) {
localSysJob = i
}
func SysLogininfor() ISysLogininfor {
if localSysLogininfor == nil {
panic("implement not found for interface ISysLogininfor, forgot register?")
}
return localSysLogininfor
}
func RegisterSysLogininfor(i ISysLogininfor) {
localSysLogininfor = i
}
func SysMenu() ISysMenu {
if localSysMenu == nil {
panic("implement not found for interface ISysMenu, forgot register?")
}
return localSysMenu
}
func RegisterSysMenu(i ISysMenu) {
localSysMenu = i
}
func SysNotice() ISysNotice {
if localSysNotice == nil {
panic("implement not found for interface ISysNotice, forgot register?")
}
return localSysNotice
}
func RegisterSysNotice(i ISysNotice) {
localSysNotice = i
}
func SysNoticeUser() ISysNoticeUser {
if localSysNoticeUser == nil {
panic("implement not found for interface ISysNoticeUser, forgot register?")
}
return localSysNoticeUser
}
func RegisterSysNoticeUser(i ISysNoticeUser) {
localSysNoticeUser = i
}
func SysOperLog() ISysOperLog {
if localSysOperLog == nil {
panic("implement not found for interface ISysOperLog, forgot register?")
}
return localSysOperLog
}
func RegisterSysOperLog(i ISysOperLog) {
localSysOperLog = i
}
func SysOss() ISysOss {
if localSysOss == nil {
panic("implement not found for interface ISysOss, forgot register?")
}
return localSysOss
}
func RegisterSysOss(i ISysOss) {
localSysOss = i
}
func SysOssConfig() ISysOssConfig {
if localSysOssConfig == nil {
panic("implement not found for interface ISysOssConfig, forgot register?")
}
return localSysOssConfig
}
func RegisterSysOssConfig(i ISysOssConfig) {
localSysOssConfig = i
}
func SysPost() ISysPost {
if localSysPost == nil {
panic("implement not found for interface ISysPost, forgot register?")
}
return localSysPost
}
func RegisterSysPost(i ISysPost) {
localSysPost = i
}
func SysQrcode() ISysQrcode {
if localSysQrcode == nil {
panic("implement not found for interface ISysQrcode, forgot register?")
}
return localSysQrcode
}
func RegisterSysQrcode(i ISysQrcode) {
localSysQrcode = i
}
func SysRole() ISysRole {
if localSysRole == nil {
panic("implement not found for interface ISysRole, forgot register?")
}
return localSysRole
}
func RegisterSysRole(i ISysRole) {
localSysRole = i
}
func SysSocial() ISysSocial {
if localSysSocial == nil {
panic("implement not found for interface ISysSocial, forgot register?")
}
return localSysSocial
}
func RegisterSysSocial(i ISysSocial) {
localSysSocial = i
}
func SysTenant() ISysTenant {
if localSysTenant == nil {
panic("implement not found for interface ISysTenant, forgot register?")
}
return localSysTenant
}
func RegisterSysTenant(i ISysTenant) {
localSysTenant = i
}
func SysTenantPackage() ISysTenantPackage {
if localSysTenantPackage == nil {
panic("implement not found for interface ISysTenantPackage, forgot register?")
}
return localSysTenantPackage
}
func RegisterSysTenantPackage(i ISysTenantPackage) {
localSysTenantPackage = i
}
func SysUser() ISysUser {
if localSysUser == nil {
panic("implement not found for interface ISysUser, forgot register?")
}
return localSysUser
}
func RegisterSysUser(i ISysUser) {
localSysUser = i
}
func SysUserOnline() ISysUserOnline {
if localSysUserOnline == nil {
panic("implement not found for interface ISysUserOnline, forgot register?")
}
return localSysUserOnline
}
func RegisterSysUserOnline(i ISysUserOnline) {
localSysUserOnline = i
}