sync: 同步 v1.0.4 并脱敏配置与镜像

This commit is contained in:
MiFaZhan
2026-10-29 14:36:49 +08:00
parent 743e8e6e6e
commit 2206a393f7
516 changed files with 18119 additions and 2253 deletions
+2 -1
View File
@@ -16,6 +16,7 @@ import (
type (
IAgentDept interface {
DeptExport(ctx context.Context, in *model.AgentDeptListParam) error
DeptList(ctx context.Context, in *model.AgentDeptListParam) (list []*model.AgentEnterpriseCustomerModel, total int, err error)
DeptView(ctx context.Context, deptId int64) (res *model.AgentEnterpriseCustomerModel, err error)
DeptSave(ctx context.Context, in *model.AgentDeptSaveParam) (deptId int64, err error)
@@ -46,12 +47,12 @@ type (
RoleSave(ctx context.Context, in *model.AgentRoleSaveParam) (roleId int64, err error)
// RoleDelete 删除代理商角色,删除前校验本级机构默认角色不可被删除。
RoleDelete(ctx context.Context, roleIds []int64) (err error)
SyncInheritedRole(ctx context.Context, message *model.AgentRoleSyncMessage) error
}
IAgentScope interface {
ScopeDeptIds(ctx context.Context) (deptIds []int64, err error)
}
IAgentUser interface {
UserExport(ctx context.Context, in *model.AgentUserListParam) error
// UserList 查询用户列表
UserList(ctx context.Context, in *model.AgentUserListParam) (list []*model.AgentUserListModel, total int, err error)
// UserView 查看用户详情
+32
View File
@@ -19,6 +19,18 @@ type (
List(ctx context.Context, in *model.BatchExecutionLogListParam) (list []*model.BatchExecutionLogListModel, totalCount int, err error)
Summary(ctx context.Context, in *model.BatchExecutionLogDetailParam) (res *model.BatchExecutionLogDetailModel, err error)
DetailList(ctx context.Context, in *model.BatchDetailListParam) (list []*model.BatchDetailListModel, totalCount int, err error)
// SyncReview 手动同步待核查批次的上游执行结果(只读补查既有受理号,不重发办理)
SyncReview(ctx context.Context, in *model.BatchExecutionLogSyncReviewParam) (res *model.BatchExecutionLogSyncReviewModel, err error)
}
ICommonDownloadCenter interface {
// List 下载中心列表(K2 仅本人 + K1 业务权限过滤)
List(ctx context.Context, in *model.DownloadCenterListParam) (list []*model.DownloadCenterListModel, totalCount int, err error)
// Recent 右上角全局入口:最近任务与进行中/已完成数量
Recent(ctx context.Context, in *model.DownloadCenterRecentParam) (res *model.DownloadCenterRecentModel, err error)
// Download 下载导出文件,直接写文件流
Download(ctx context.Context, in *model.DownloadCenterTaskParam) (err error)
// Remove 删除导出任务与文件
Remove(ctx context.Context, in *model.DownloadCenterTaskParam) (err error)
}
ICommonBatchMain interface {
// Model 批次主表ORM模型
@@ -26,6 +38,8 @@ type (
// List 获取批次主表列表
List(ctx context.Context, in *model.BatchMainListParam) (list []*model.BatchMainListModel, totalCount int, err error)
SuccessList(ctx context.Context, in *model.BatchMainSuccessListParam) (list []*model.BatchMainSuccessListModel, totalCount int, err error)
// ExportWarehousing 导出入库批次列表(数据与入库管理页一致)
ExportWarehousing(ctx context.Context, in *model.BatchMainSuccessListParam) (err error)
ListCards(ctx context.Context, in *model.BatchCardListParam) (list []*model.BatchCardListModel, totalCount int, err error)
// Export 导出批次主表
Export(ctx context.Context, in *model.BatchMainListParam) (err error)
@@ -222,6 +236,12 @@ type (
Delete(ctx context.Context, in *model.TMessageLogDeleteParam) (err error)
// View 获取消息日志指定信息,并标记已读
View(ctx context.Context, in *model.TMessageLogViewParam) (res *model.TMessageLogViewModel, err error)
// UnreadList 获取当前登录用户的未读消息通知
UnreadList(ctx context.Context, in *model.TMessageLogUnreadListParam) (list []*model.TMessageLogUnreadListModel, totalCount int, err error)
// Read 标记当前登录用户指定消息已读
Read(ctx context.Context, in *model.TMessageLogReadParam) (err error)
// ReadAll 标记当前登录用户全部消息已读
ReadAll(ctx context.Context, in *model.TMessageLogReadAllParam) (err error)
}
ICommonTNotifyLog interface {
// Model 通知日志ORM模型
@@ -316,6 +336,7 @@ type (
var (
localCommonBatchExecutionLog ICommonBatchExecutionLog
localCommonDownloadCenter ICommonDownloadCenter
localCommonBatchMain ICommonBatchMain
localCommonAgentBalance ICommonAgentBalance
localCommonTBalanceDetail ICommonTBalanceDetail
@@ -345,6 +366,17 @@ func RegisterCommonBatchExecutionLog(i ICommonBatchExecutionLog) {
localCommonBatchExecutionLog = i
}
func CommonDownloadCenter() ICommonDownloadCenter {
if localCommonDownloadCenter == nil {
panic("implement not found for interface ICommonDownloadCenter, forgot register?")
}
return localCommonDownloadCenter
}
func RegisterCommonDownloadCenter(i ICommonDownloadCenter) {
localCommonDownloadCenter = i
}
func CommonBatchMain() ICommonBatchMain {
if localCommonBatchMain == nil {
panic("implement not found for interface ICommonBatchMain, forgot register?")
@@ -0,0 +1,159 @@
package fullsyncretry
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
)
func TestReplayAndStatusProxy(t *testing.T) {
calls := 0
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
calls++
if r.Header.Get("Authorization") != "" {
t.Error("Admin JWT must not be forwarded to Worker")
}
if r.Method == "POST" {
var body map[string]any
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
t.Error(err)
}
if len(body) != 1 || body["requestedBy"] != "42" {
t.Errorf("unexpected Worker request: %+v", body)
}
w.WriteHeader(http.StatusAccepted)
_, _ = w.Write([]byte(`{"replayRunId":"replay-1","status":"accepted"}`))
return
}
_, _ = w.Write([]byte(`{"replayRunId":"replay-1","sourceRunId":"source","status":"completed","remaining":0,"enqueued":3}`))
}))
defer server.Close()
client := Client{BaseURL: server.URL}
r, err := client.Replay(context.Background(), "source", "42", 0)
if err != nil || r.ReplayRunID != "replay-1" {
t.Fatalf("replay: %+v %v", r, err)
}
s, err := client.Get(context.Background(), "replay-1")
if err != nil || s.Enqueued != 3 || calls != 2 {
t.Fatalf("status: %+v %v, calls=%d", s, err, calls)
}
}
func TestRunQueriesAndFailures(t *testing.T) {
calls := 0
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
calls++
switch {
case r.Method == http.MethodGet && r.URL.Path == "/internal/v1/full-sync-runs":
if r.URL.Query().Get("syncKind") != "traffic_usage" || r.URL.Query().Get("limit") != "7" {
t.Fatalf("query = %s", r.URL.RawQuery)
}
_, _ = w.Write([]byte(`{"items":[{"source":"live","run":{"runId":"run-1","operation":"current_month_usage_full_sync","status":"running"}}],"total":1}`))
case r.Method == http.MethodGet && r.URL.Path == "/internal/v1/full-sync-runs/run-1":
_, _ = w.Write([]byte(`{"source":"archive","run":{"runId":"run-1","operation":"current_month_usage_full_sync","status":"completed"},"rounds":[]}`))
case r.Method == http.MethodGet && r.URL.Path == "/internal/v1/full-sync-runs/run-1/failures":
_, _ = w.Write([]byte(`{"items":[{"taskId":"task-1","cardId":1,"errorCategory":"business","errorMessage":"failed","round":2}],"nextCursor":"next"}`))
default:
http.NotFound(w, r)
}
}))
defer server.Close()
client := Client{BaseURL: server.URL}
list, err := client.ListRuns(context.Background(), "traffic_usage", 7)
if err != nil || list.Total != 1 || len(list.Items) != 1 {
t.Fatalf("list = %+v, err = %v", list, err)
}
view, err := client.Run(context.Background(), "run-1")
if err != nil || view.Source != "archive" {
t.Fatalf("run = %+v, err = %v", view, err)
}
page, err := client.Failures(context.Background(), "run-1", 100, "cursor")
if err != nil || len(page.Items) != 1 || page.NextCursor != "next" || calls != 3 {
t.Fatalf("failures = %+v, err = %v, calls = %d", page, err, calls)
}
}
func TestReplayWorkerTimeout(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { time.Sleep(50 * time.Millisecond) }))
defer server.Close()
client := Client{BaseURL: server.URL, HTTP: &http.Client{Timeout: time.Millisecond}}
_, err := client.Replay(context.Background(), "source", "42", 0)
if err == nil || !strings.Contains(err.Error(), "Worker unavailable") {
t.Fatalf("timeout: %v", err)
}
}
func TestReplayValidationAndWorkerErrors(t *testing.T) {
calls := 0
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
calls++
w.WriteHeader(http.StatusConflict)
_, _ = w.Write([]byte(`{"error":{"message":"ICCID secret"}}`))
}))
defer server.Close()
client := Client{BaseURL: server.URL}
for _, id := range []string{"", "a/b", "{bad}", strings.Repeat("a", 129)} {
if _, err := client.Replay(context.Background(), id, "42", 0); gerror.Code(err) != gcode.CodeInvalidParameter {
t.Fatalf("id=%q: %v", id, err)
}
}
if calls != 0 {
t.Fatal("invalid source triggered Worker")
}
// The edge must reject an oversized round budget before calling Worker.
for _, rounds := range []int{-1, MaxReplayRounds + 1} {
if _, err := client.Replay(context.Background(), "source", "42", rounds); gerror.Code(err) != gcode.CodeInvalidParameter {
t.Fatalf("maxRounds=%d: %v", rounds, err)
}
}
if calls != 0 {
t.Fatal("invalid maxRounds triggered Worker")
}
_, err := client.Replay(context.Background(), "source", "42", 0)
if gerror.Code(err) != gcode.CodeInvalidParameter || strings.Contains(err.Error(), "ICCID") {
t.Fatalf("error leaked Worker body: %v", err)
}
for _, status := range []int{400, 401, 404, 409, 422, 500} {
if workerError(status) == nil {
t.Fatalf("missing error mapping for %d", status)
}
}
}
// The round budget must reach Worker only when the caller asks for one, so an
// omitted value keeps the Worker-side configured default authoritative.
func TestReplayForwardsMaxRoundsOnlyWhenSet(t *testing.T) {
var bodies []map[string]any
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var body map[string]any
_ = json.NewDecoder(r.Body).Decode(&body)
bodies = append(bodies, body)
w.WriteHeader(http.StatusAccepted)
_, _ = w.Write([]byte(`{"replayRunId":"replay-1","status":"accepted"}`))
}))
defer server.Close()
client := Client{BaseURL: server.URL}
if _, err := client.Replay(context.Background(), "source", "42", 0); err != nil {
t.Fatal(err)
}
if _, err := client.Replay(context.Background(), "source", "42", 7); err != nil {
t.Fatal(err)
}
if len(bodies) != 2 {
t.Fatalf("expected 2 Worker calls, got %d", len(bodies))
}
if _, present := bodies[0]["maxRounds"]; present {
t.Fatalf("omitted budget must not be sent: %+v", bodies[0])
}
if got := bodies[1]["maxRounds"]; got != float64(7) {
t.Fatalf("explicit budget must be forwarded, got %+v", bodies[1])
}
}
@@ -0,0 +1,39 @@
package fullsyncretry
import (
"context"
"net/http"
"net/http/httptest"
"testing"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
)
func TestListRunsAcceptsFiveKindsAndRejectsUnknownFilters(t *testing.T) {
var filters []string
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
filters = append(filters, r.URL.Query().Get("syncKind"))
_, _ = w.Write([]byte(`{"items":null,"total":0}`))
}))
defer server.Close()
client := Client{BaseURL: server.URL}
kinds := []string{"", "traffic_usage", "sim_status", "imei", "traffic_usage_daily", "traffic_usage_monthly"}
for i, kind := range kinds {
result, err := client.ListRuns(context.Background(), kind, 100)
if err != nil || result.Items == nil || len(filters) != i+1 || filters[i] != kind {
t.Fatalf("kind=%q result=%+v filters=%v error=%v", kind, result, filters, err)
}
}
for _, query := range []struct {
kind string
limit int
}{{"unknown", 10}, {"IMEI", 10}, {"", -1}, {"imei", 101}} {
if _, err := client.ListRuns(context.Background(), query.kind, query.limit); gerror.Code(err) != gcode.CodeInvalidParameter {
t.Fatalf("invalid filter %+v: %v", query, err)
}
}
if len(filters) != len(kinds) {
t.Fatal("invalid filter reached Worker")
}
}
+1
View File
@@ -13,6 +13,7 @@ type (
ScanExpire(ctx context.Context)
PreloadExpireCandidates(ctx context.Context)
ScanExpireCandidates(ctx context.Context)
NotifyMonthlyExpiry(ctx context.Context)
ConfigList(ctx context.Context, in *model.SimWarningConfigListParam) (list []*model.SimWarningConfigModel, totalCount int, err error)
ConfigView(ctx context.Context, in *model.SimWarningConfigViewParam) (res *model.SimWarningConfigModel, err error)
ConfigSave(ctx context.Context, in *model.SimWarningConfigSaveParam) (res *model.SimWarningConfigSaveModel, err error)
+1 -1
View File
@@ -208,7 +208,7 @@ type (
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)
UploadLocalWithFileName(ctx context.Context, file *ghttp.UploadFile, newFileType int, fileName string, subDirName string, notAddDate int) (result model.UploadResponse, err error)
DownloadLocal(ctx context.Context, file *entity.SysOss) (err error)
DownloadLocal(ctx context.Context, file *entity.SysOss, beforeSend ...func() error) (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)