Ver código fonte

feat:1.新增应用管理

wangkun 1 semana atrás
pai
commit
823b3e33ca

+ 32 - 0
internal/handler/application/addapplicationhandler.go

@@ -0,0 +1,32 @@
+// Code scaffolded by goctl. Safe to edit.
+// goctl 1.10.2
+
+package application
+
+import (
+	"net/http"
+
+	"github.com/zeromicro/go-zero/rest/httpx"
+	"udeServer/internal/logic/application"
+	"udeServer/internal/svc"
+	"udeServer/internal/types"
+)
+
+// 添加应用凭证
+func AddApplicationHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
+		var req types.ApplicationCredentialsAddReq
+		if err := httpx.Parse(r, &req); err != nil {
+			httpx.ErrorCtx(r.Context(), w, err)
+			return
+		}
+
+		l := application.NewAddApplicationLogic(r.Context(), svcCtx)
+		resp, err := l.AddApplication(&req)
+		if err != nil {
+			httpx.ErrorCtx(r.Context(), w, err)
+		} else {
+			httpx.OkJsonCtx(r.Context(), w, resp)
+		}
+	}
+}

+ 32 - 0
internal/handler/application/applicationlisthandler.go

@@ -0,0 +1,32 @@
+// Code scaffolded by goctl. Safe to edit.
+// goctl 1.10.2
+
+package application
+
+import (
+	"net/http"
+
+	"github.com/zeromicro/go-zero/rest/httpx"
+	"udeServer/internal/logic/application"
+	"udeServer/internal/svc"
+	"udeServer/internal/types"
+)
+
+// 应用凭证列表
+func ApplicationListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
+		var req types.ApplicationListReq
+		if err := httpx.Parse(r, &req); err != nil {
+			httpx.ErrorCtx(r.Context(), w, err)
+			return
+		}
+
+		l := application.NewApplicationListLogic(r.Context(), svcCtx)
+		resp, err := l.ApplicationList(&req)
+		if err != nil {
+			httpx.ErrorCtx(r.Context(), w, err)
+		} else {
+			httpx.OkJsonCtx(r.Context(), w, resp)
+		}
+	}
+}

+ 32 - 0
internal/handler/application/deleteapplicationhandler.go

@@ -0,0 +1,32 @@
+// Code scaffolded by goctl. Safe to edit.
+// goctl 1.10.2
+
+package application
+
+import (
+	"net/http"
+
+	"github.com/zeromicro/go-zero/rest/httpx"
+	"udeServer/internal/logic/application"
+	"udeServer/internal/svc"
+	"udeServer/internal/types"
+)
+
+// 删除应用凭证
+func DeleteApplicationHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
+		var req types.ApplicationCredentialsDeleteReq
+		if err := httpx.Parse(r, &req); err != nil {
+			httpx.ErrorCtx(r.Context(), w, err)
+			return
+		}
+
+		l := application.NewDeleteApplicationLogic(r.Context(), svcCtx)
+		resp, err := l.DeleteApplication(&req)
+		if err != nil {
+			httpx.ErrorCtx(r.Context(), w, err)
+		} else {
+			httpx.OkJsonCtx(r.Context(), w, resp)
+		}
+	}
+}

+ 32 - 0
internal/handler/application/distributeapisecrethandler.go

@@ -0,0 +1,32 @@
+// Code scaffolded by goctl. Safe to edit.
+// goctl 1.10.2
+
+package application
+
+import (
+	"net/http"
+
+	"github.com/zeromicro/go-zero/rest/httpx"
+	"udeServer/internal/logic/application"
+	"udeServer/internal/svc"
+	"udeServer/internal/types"
+)
+
+// 下发应用凭证密钥
+func DistributeApiSecretHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
+		var req types.ApplicationCredentialsDistributeReq
+		if err := httpx.Parse(r, &req); err != nil {
+			httpx.ErrorCtx(r.Context(), w, err)
+			return
+		}
+
+		l := application.NewDistributeApiSecretLogic(r.Context(), svcCtx)
+		resp, err := l.DistributeApiSecret(&req)
+		if err != nil {
+			httpx.ErrorCtx(r.Context(), w, err)
+		} else {
+			httpx.OkJsonCtx(r.Context(), w, resp)
+		}
+	}
+}

+ 32 - 0
internal/handler/application/resetapplicationhandler.go

@@ -0,0 +1,32 @@
+// Code scaffolded by goctl. Safe to edit.
+// goctl 1.10.2
+
+package application
+
+import (
+	"net/http"
+
+	"github.com/zeromicro/go-zero/rest/httpx"
+	"udeServer/internal/logic/application"
+	"udeServer/internal/svc"
+	"udeServer/internal/types"
+)
+
+// 重置应用凭证密钥
+func ResetApplicationHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
+		var req types.ApplicationCredentialsResetReq
+		if err := httpx.Parse(r, &req); err != nil {
+			httpx.ErrorCtx(r.Context(), w, err)
+			return
+		}
+
+		l := application.NewResetApplicationLogic(r.Context(), svcCtx)
+		resp, err := l.ResetApplication(&req)
+		if err != nil {
+			httpx.ErrorCtx(r.Context(), w, err)
+		} else {
+			httpx.OkJsonCtx(r.Context(), w, resp)
+		}
+	}
+}

+ 32 - 0
internal/handler/application/updateapplicationstatushandler.go

@@ -0,0 +1,32 @@
+// Code scaffolded by goctl. Safe to edit.
+// goctl 1.10.2
+
+package application
+
+import (
+	"net/http"
+
+	"github.com/zeromicro/go-zero/rest/httpx"
+	"udeServer/internal/logic/application"
+	"udeServer/internal/svc"
+	"udeServer/internal/types"
+)
+
+// 切换应用凭证状态(启用/禁用互换)
+func UpdateApplicationStatusHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
+		var req types.ApplicationCredentialsUpdateStatusReq
+		if err := httpx.Parse(r, &req); err != nil {
+			httpx.ErrorCtx(r.Context(), w, err)
+			return
+		}
+
+		l := application.NewUpdateApplicationStatusLogic(r.Context(), svcCtx)
+		resp, err := l.UpdateApplicationStatus(&req)
+		if err != nil {
+			httpx.ErrorCtx(r.Context(), w, err)
+		} else {
+			httpx.OkJsonCtx(r.Context(), w, resp)
+		}
+	}
+}

+ 43 - 0
internal/handler/routes.go

@@ -6,6 +6,7 @@ package handler
 import (
 import (
 	"net/http"
 	"net/http"
 
 
+	application "udeServer/internal/handler/application"
 	systemlog "udeServer/internal/handler/systemlog"
 	systemlog "udeServer/internal/handler/systemlog"
 	"udeServer/internal/svc"
 	"udeServer/internal/svc"
 
 
@@ -114,6 +115,48 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
 		rest.WithPrefix("/api/v1"),
 		rest.WithPrefix("/api/v1"),
 	)
 	)
 
 
+	server.AddRoutes(
+		[]rest.Route{
+			{
+				// 添加应用凭证
+				Method:  http.MethodPost,
+				Path:    "/addApplication",
+				Handler: application.AddApplicationHandler(serverCtx),
+			},
+			{
+				// 应用凭证列表
+				Method:  http.MethodPost,
+				Path:    "/applicationList",
+				Handler: application.ApplicationListHandler(serverCtx),
+			},
+			{
+				// 删除应用凭证
+				Method:  http.MethodPost,
+				Path:    "/deleteApplication",
+				Handler: application.DeleteApplicationHandler(serverCtx),
+			},
+			{
+				// 下发应用凭证密钥
+				Method:  http.MethodPost,
+				Path:    "/distributeApiSecret",
+				Handler: application.DistributeApiSecretHandler(serverCtx),
+			},
+			{
+				// 重置应用凭证密钥
+				Method:  http.MethodPost,
+				Path:    "/resetApplication",
+				Handler: application.ResetApplicationHandler(serverCtx),
+			},
+			{
+				// 切换应用凭证状态(启用/禁用互换)
+				Method:  http.MethodPost,
+				Path:    "/updateApplicationStatus",
+				Handler: application.UpdateApplicationStatusHandler(serverCtx),
+			},
+		},
+		rest.WithPrefix("/api/v1/applicationCredentials"),
+	)
+
 	server.AddRoutes(
 	server.AddRoutes(
 		[]rest.Route{
 		[]rest.Route{
 			{
 			{

+ 78 - 0
internal/logic/application/addapplicationlogic.go

@@ -0,0 +1,78 @@
+// Code scaffolded by goctl. Safe to edit.
+// goctl 1.10.2
+
+package application
+
+import (
+	"context"
+	"database/sql"
+	"strings"
+
+	"udeServer/internal/apierr"
+	"udeServer/internal/audit"
+	"udeServer/internal/log"
+	"udeServer/internal/model"
+	"udeServer/internal/svc"
+	"udeServer/internal/types"
+
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type AddApplicationLogic struct {
+	logx.Logger
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+}
+
+// 添加应用凭证
+func NewAddApplicationLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AddApplicationLogic {
+	return &AddApplicationLogic{
+		Logger: logx.WithContext(ctx),
+		ctx:    ctx,
+		svcCtx: svcCtx,
+	}
+}
+
+func (l *AddApplicationLogic) AddApplication(req *types.ApplicationCredentialsAddReq) (*types.ApplicationCredentialsAddResp, error) {
+	if strings.TrimSpace(req.Name) == "" {
+		return nil, apierr.BadRequest("应用名称不能为空")
+	}
+	// 状态缺省启用;只接受 1(启用)/2(禁用)
+	if req.Status == 0 {
+		req.Status = 1
+	}
+	if req.Status != 1 && req.Status != 2 {
+		return nil, apierr.BadRequest("应用状态只允许 1(启用) 或 2(禁用)")
+	}
+
+	exists, err := l.svcCtx.ApplicationCredModel.ExistsByName(l.ctx, req.Name)
+	if err != nil {
+		log.RecordError(l.ctx, err)
+		return nil, apierr.New(500, "添加应用凭证失败")
+	}
+	if exists {
+		return nil, apierr.BadRequest("已存在同名[%s]的应用凭证", req.Name)
+	}
+
+	credIden := audit.NewUUID()
+	data := &model.TApplicationCredentials{
+		CredIden:  credIden,
+		Name:      req.Name,
+		ApiKey:    GenerateApiKey(),
+		ApiSecret: GenerateApiSecret(),
+		HmacAlgo:  HmacAlgo,
+		Status:    int64(req.Status),
+		Description: sql.NullString{
+			String: req.Description,
+			Valid:  req.Description != "",
+		},
+		CreatedBy: operatorName,
+		UpdatedBy: operatorName,
+	}
+	if _, err := l.svcCtx.ApplicationCredModel.Insert(l.ctx, data); err != nil {
+		log.RecordError(l.ctx, err)
+		return nil, apierr.New(500, "添加应用凭证失败")
+	}
+
+	return &types.ApplicationCredentialsAddResp{Iden: credIden}, nil
+}

+ 57 - 0
internal/logic/application/applicationlistlogic.go

@@ -0,0 +1,57 @@
+// Code scaffolded by goctl. Safe to edit.
+// goctl 1.10.2
+
+package application
+
+import (
+	"context"
+
+	"udeServer/internal/apierr"
+	"udeServer/internal/log"
+	"udeServer/internal/svc"
+	"udeServer/internal/types"
+
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type ApplicationListLogic struct {
+	logx.Logger
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+}
+
+// 应用凭证列表
+func NewApplicationListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ApplicationListLogic {
+	return &ApplicationListLogic{
+		Logger: logx.WithContext(ctx),
+		ctx:    ctx,
+		svcCtx: svcCtx,
+	}
+}
+
+func (l *ApplicationListLogic) ApplicationList(req *types.ApplicationListReq) (*types.ApplicationListResp, error) {
+	page, pageSize := normalizePage(req.Page, req.PageSize)
+
+	// 状态筛选仅接受 1(启用)/2(禁用);其余取值(含 0)视为不过滤,与 sdf 行为一致
+	var filterStatus int64
+	switch req.Status {
+	case 1, 2:
+		filterStatus = req.Status
+	}
+
+	rows, total, err := l.svcCtx.ApplicationCredModel.FindPage(l.ctx, page, pageSize, filterStatus, req.CredIden, req.Name)
+	if err != nil {
+		log.RecordError(l.ctx, err)
+		return nil, apierr.New(500, "查询应用凭证列表失败")
+	}
+
+	lists := make([]types.ApplicationCredentials, len(rows))
+	for i := range rows {
+		lists[i] = toApplicationCredentials(&rows[i])
+	}
+
+	return &types.ApplicationListResp{
+		Total:            total,
+		ApplicationLists: lists,
+	}, nil
+}

+ 61 - 0
internal/logic/application/applicationutil.go

@@ -0,0 +1,61 @@
+package application
+
+import (
+	"math/big"
+	"strings"
+
+	"udeServer/internal/apierr"
+
+	"github.com/google/uuid"
+	"github.com/mattheath/base62"
+	"github.com/segmentio/ksuid"
+)
+
+// 应用凭证签名算法,与 audit 包保持同一算法字面量(sdf 同源)。
+const HmacAlgo = "HmacSM3"
+
+// operatorName 无用户体系时的操作人占位,与日志归档/恢复一致。
+const operatorName = "admin"
+
+// GenerateApiKey 生成对外公钥(KSUID,27 位可排序 ID)。
+func GenerateApiKey() string {
+	return ksuid.New().String()
+}
+
+// GenerateApiSecret 生成原始密钥(uuid 16 字节转 base62,22 字符)。
+func GenerateApiSecret() string {
+	u := uuid.New()
+	bi := new(big.Int).SetBytes(u[:])
+	return base62.EncodeBigInt(bi)
+}
+
+// maskSensitive 掩码展示:前 4 后 4 明文,中间打星;长度不大于 8 时原样返回。
+func maskSensitive(value string) string {
+	if len(value) <= 8 {
+		return value
+	}
+	return value[:4] + strings.Repeat("*", len(value)-8) + value[len(value)-4:]
+}
+
+// normalizePage 页码/页大小兜底:1/10 起,上限 100。
+func normalizePage(page, pageSize int64) (int64, int64) {
+	if page < 1 {
+		page = 1
+	}
+	if pageSize < 10 {
+		pageSize = 10
+	}
+	if pageSize > 100 {
+		pageSize = 100
+	}
+	return page, pageSize
+}
+
+// checkId 主键合法性,与 logic 包同名 helper 行为一致。
+func checkId(id int64) error {
+	if id <= 0 {
+		return apierr.BadRequest("id must be greater than 0")
+	}
+
+	return nil
+}

+ 34 - 0
internal/logic/application/convert.go

@@ -0,0 +1,34 @@
+package application
+
+import (
+	"time"
+
+	"udeServer/internal/model"
+	"udeServer/internal/types"
+)
+
+// toApplicationCredentials 将模型行转换为响应结构;ApiSecret 做掩码,原文只经下发接口输出。
+func toApplicationCredentials(row *model.TApplicationCredentials) types.ApplicationCredentials {
+	return types.ApplicationCredentials{
+		Id:          row.Id,
+		CredIden:    row.CredIden,
+		Name:        row.Name,
+		ApiKey:      row.ApiKey,
+		ApiSecret:   maskSensitive(row.ApiSecret),
+		Status:      int(row.Status),
+		Description: row.Description.String,
+		CreatedBy:   row.CreatedBy,
+		CreatedAt:   formatCredentialTime(row.CreatedAt),
+		UpdatedBy:   row.UpdatedBy,
+		UpdatedAt:   formatCredentialTime(row.UpdatedAt),
+	}
+}
+
+// formatCredentialTime 时间展示格式与 sdf-serve 保持一致。
+func formatCredentialTime(t time.Time) string {
+	if t.IsZero() {
+		return ""
+	}
+
+	return t.Format("2006-01-02 15:04:05")
+}

+ 54 - 0
internal/logic/application/deleteapplicationlogic.go

@@ -0,0 +1,54 @@
+// Code scaffolded by goctl. Safe to edit.
+// goctl 1.10.2
+
+package application
+
+import (
+	"context"
+	"errors"
+
+	"udeServer/internal/apierr"
+	"udeServer/internal/log"
+	"udeServer/internal/model"
+	"udeServer/internal/svc"
+	"udeServer/internal/types"
+
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type DeleteApplicationLogic struct {
+	logx.Logger
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+}
+
+// 删除应用凭证
+func NewDeleteApplicationLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeleteApplicationLogic {
+	return &DeleteApplicationLogic{
+		Logger: logx.WithContext(ctx),
+		ctx:    ctx,
+		svcCtx: svcCtx,
+	}
+}
+
+func (l *DeleteApplicationLogic) DeleteApplication(req *types.ApplicationCredentialsDeleteReq) (*types.ApplicationCredentialsDeleteResp, error) {
+	if err := checkId(req.Id); err != nil {
+		return nil, err
+	}
+
+	cred, err := l.svcCtx.ApplicationCredModel.FindOne(l.ctx, req.Id)
+	if errors.Is(err, model.ErrNotFound) {
+		return nil, apierr.NotFound("application credential", req.Id)
+	}
+	if err != nil {
+		log.RecordError(l.ctx, err)
+		return nil, apierr.New(500, "查询应用凭证失败")
+	}
+
+	if err := l.svcCtx.ApplicationCredModel.Delete(l.ctx, req.Id); err != nil {
+		log.RecordError(l.ctx, err)
+		return nil, apierr.New(500, "删除应用凭证失败")
+	}
+
+	return &types.ApplicationCredentialsDeleteResp{Iden: cred.CredIden}, nil
+}

+ 50 - 0
internal/logic/application/distributeapisecretlogic.go

@@ -0,0 +1,50 @@
+// Code scaffolded by goctl. Safe to edit.
+// goctl 1.10.2
+
+package application
+
+import (
+	"context"
+	"errors"
+
+	"udeServer/internal/apierr"
+	"udeServer/internal/log"
+	"udeServer/internal/model"
+	"udeServer/internal/svc"
+	"udeServer/internal/types"
+
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type DistributeApiSecretLogic struct {
+	logx.Logger
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+}
+
+// 下发应用凭证密钥
+func NewDistributeApiSecretLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DistributeApiSecretLogic {
+	return &DistributeApiSecretLogic{
+		Logger: logx.WithContext(ctx),
+		ctx:    ctx,
+		svcCtx: svcCtx,
+	}
+}
+
+func (l *DistributeApiSecretLogic) DistributeApiSecret(req *types.ApplicationCredentialsDistributeReq) (*types.ApplicationCredentialsDistributeResp, error) {
+	if err := checkId(req.Id); err != nil {
+		return nil, err
+	}
+
+	cred, err := l.svcCtx.ApplicationCredModel.FindOne(l.ctx, req.Id)
+	if errors.Is(err, model.ErrNotFound) {
+		return nil, apierr.NotFound("application credential", req.Id)
+	}
+	if err != nil {
+		log.RecordError(l.ctx, err)
+		return nil, apierr.New(500, "查询应用凭证失败")
+	}
+
+	// 仅此接口返回密钥原文
+	return &types.ApplicationCredentialsDistributeResp{ApiSecret: cred.ApiSecret}, nil
+}

+ 57 - 0
internal/logic/application/resetapplicationlogic.go

@@ -0,0 +1,57 @@
+// Code scaffolded by goctl. Safe to edit.
+// goctl 1.10.2
+
+package application
+
+import (
+	"context"
+	"errors"
+
+	"udeServer/internal/apierr"
+	"udeServer/internal/log"
+	"udeServer/internal/model"
+	"udeServer/internal/svc"
+	"udeServer/internal/types"
+
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type ResetApplicationLogic struct {
+	logx.Logger
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+}
+
+// 重置应用凭证密钥
+func NewResetApplicationLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ResetApplicationLogic {
+	return &ResetApplicationLogic{
+		Logger: logx.WithContext(ctx),
+		ctx:    ctx,
+		svcCtx: svcCtx,
+	}
+}
+
+func (l *ResetApplicationLogic) ResetApplication(req *types.ApplicationCredentialsResetReq) (*types.ApplicationCredentialsResetResp, error) {
+	if err := checkId(req.Id); err != nil {
+		return nil, err
+	}
+
+	cred, err := l.svcCtx.ApplicationCredModel.FindOne(l.ctx, req.Id)
+	if errors.Is(err, model.ErrNotFound) {
+		return nil, apierr.NotFound("application credential", req.Id)
+	}
+	if err != nil {
+		log.RecordError(l.ctx, err)
+		return nil, apierr.New(500, "查询应用凭证失败")
+	}
+
+	// 只换密钥,apiKey/状态等保持不变
+	cred.ApiSecret = GenerateApiSecret()
+	cred.UpdatedBy = operatorName
+	if err := l.svcCtx.ApplicationCredModel.Update(l.ctx, cred); err != nil {
+		log.RecordError(l.ctx, err)
+		return nil, apierr.New(500, "重置应用凭证密钥失败")
+	}
+
+	return &types.ApplicationCredentialsResetResp{Iden: cred.CredIden}, nil
+}

+ 63 - 0
internal/logic/application/updateapplicationstatuslogic.go

@@ -0,0 +1,63 @@
+// Code scaffolded by goctl. Safe to edit.
+// goctl 1.10.2
+
+package application
+
+import (
+	"context"
+	"errors"
+
+	"udeServer/internal/apierr"
+	"udeServer/internal/log"
+	"udeServer/internal/model"
+	"udeServer/internal/svc"
+	"udeServer/internal/types"
+
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type UpdateApplicationStatusLogic struct {
+	logx.Logger
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+}
+
+// 切换应用凭证状态(启用/禁用互换)
+func NewUpdateApplicationStatusLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateApplicationStatusLogic {
+	return &UpdateApplicationStatusLogic{
+		Logger: logx.WithContext(ctx),
+		ctx:    ctx,
+		svcCtx: svcCtx,
+	}
+}
+
+func (l *UpdateApplicationStatusLogic) UpdateApplicationStatus(req *types.ApplicationCredentialsUpdateStatusReq) (*types.ApplicationCredentialsUpdateStatusResp, error) {
+	if err := checkId(req.Id); err != nil {
+		return nil, err
+	}
+
+	cred, err := l.svcCtx.ApplicationCredModel.FindOne(l.ctx, req.Id)
+	if errors.Is(err, model.ErrNotFound) {
+		return nil, apierr.NotFound("application credential", req.Id)
+	}
+	if err != nil {
+		log.RecordError(l.ctx, err)
+		return nil, apierr.New(500, "查询应用凭证失败")
+	}
+
+	// 启用/禁用互换:1<->2;其他状态视为数据异常
+	toggle := map[int64]int64{1: 2, 2: 1}
+	newStatus, ok := toggle[cred.Status]
+	if !ok {
+		return nil, apierr.BadRequest("当前应用状态(status=%d)不支持切换", cred.Status)
+	}
+
+	cred.Status = newStatus
+	cred.UpdatedBy = operatorName
+	if err := l.svcCtx.ApplicationCredModel.Update(l.ctx, cred); err != nil {
+		log.RecordError(l.ctx, err)
+		return nil, apierr.New(500, "更新应用凭证状态失败")
+	}
+
+	return &types.ApplicationCredentialsUpdateStatusResp{Iden: cred.CredIden}, nil
+}

+ 161 - 0
internal/model/tapplicationcredentialsmodel.go

@@ -0,0 +1,161 @@
+package model
+
+import (
+	"context"
+	"database/sql"
+	"fmt"
+	"strings"
+	"time"
+
+	"github.com/zeromicro/go-zero/core/stores/sqlx"
+)
+
+var _ TApplicationCredentialsModel = (*defaultTApplicationCredentialsModel)(nil)
+
+type (
+	TApplicationCredentials struct {
+		Id          int64          `db:"id"`          // 凭证 ID
+		CredIden    string         `db:"cred_iden"`   // 应用标识
+		Name        string         `db:"name"`        // 应用名称
+		ApiKey      string         `db:"api_key"`     // 对外公钥
+		ApiSecret   string         `db:"api_secret"`  // HMAC 原始密钥(明文存储)
+		HmacAlgo    string         `db:"hmac_algo"`   // 签名算法
+		Status      int64          `db:"status"`      // 启用状态:1=启用,2=禁用
+		Description sql.NullString `db:"description"` // 应用描述
+		CreatedBy   string         `db:"created_by"`  // 创建人
+		CreatedAt   time.Time      `db:"created_at"`  // 创建时间
+		UpdatedBy   string         `db:"updated_by"`  // 更新人
+		UpdatedAt   time.Time      `db:"updated_at"`  // 更新时间
+	}
+
+	TApplicationCredentialsModel interface {
+		Insert(ctx context.Context, data *TApplicationCredentials) (sql.Result, error)
+		FindOne(ctx context.Context, id int64) (*TApplicationCredentials, error)
+		ExistsByName(ctx context.Context, name string) (bool, error)
+		Update(ctx context.Context, data *TApplicationCredentials) error
+		Delete(ctx context.Context, id int64) error
+		FindPage(ctx context.Context, page, pageSize int64, status int64, credIden, name string) ([]TApplicationCredentials, int64, error)
+	}
+)
+
+// tApplicationCredentialsRows is the full column list.
+var tApplicationCredentialsRows = strings.Join([]string{
+	"`id`", "`cred_iden`", "`name`", "`api_key`", "`api_secret`", "`hmac_algo`",
+	"`status`", "`description`", "`created_by`", "`created_at`",
+	"`updated_by`", "`updated_at`",
+}, ", ")
+
+type defaultTApplicationCredentialsModel struct {
+	conn  sqlx.SqlConn
+	table string
+}
+
+// NewTApplicationCredentialsModel returns a model for the database table.
+func NewTApplicationCredentialsModel(conn sqlx.SqlConn) TApplicationCredentialsModel {
+	return &defaultTApplicationCredentialsModel{
+		conn:  conn,
+		table: "`t_application_credentials`",
+	}
+}
+
+// Insert stores one credential row; CreatedAt/UpdatedAt are filled by
+// DEFAULT CURRENT_TIMESTAMP, updated_at also auto-refreshes via ON UPDATE.
+func (m *defaultTApplicationCredentialsModel) Insert(ctx context.Context, data *TApplicationCredentials) (sql.Result, error) {
+	query := fmt.Sprintf("insert into %s (`cred_iden`, `name`, `api_key`, `api_secret`, `hmac_algo`, `status`, `description`, `created_by`, `updated_by`) values (?, ?, ?, ?, ?, ?, ?, ?, ?)",
+		m.table)
+	ret, err := m.conn.ExecCtx(ctx, query,
+		data.CredIden, data.Name, data.ApiKey, data.ApiSecret, data.HmacAlgo,
+		data.Status, data.Description, data.CreatedBy, data.UpdatedBy)
+	return ret, err
+}
+
+// FindOne looks a credential up by its auto-increment id.
+func (m *defaultTApplicationCredentialsModel) FindOne(ctx context.Context, id int64) (*TApplicationCredentials, error) {
+	query := fmt.Sprintf("select %s from %s where `id` = ? limit 1", tApplicationCredentialsRows, m.table)
+	var resp TApplicationCredentials
+	err := m.conn.QueryRowCtx(ctx, &resp, query, id)
+	switch err {
+	case nil:
+		return &resp, nil
+	case sqlx.ErrNotFound:
+		return nil, ErrNotFound
+	default:
+		return nil, err
+	}
+}
+
+// ExistsByName reports whether a credential with the same name exists.
+func (m *defaultTApplicationCredentialsModel) ExistsByName(ctx context.Context, name string) (bool, error) {
+	query := fmt.Sprintf("select count(1) from %s where `name` = ?", m.table)
+	var count int64
+	err := m.conn.QueryRowCtx(ctx, &count, query, name)
+	switch err {
+	case nil:
+		return count > 0, nil
+	case sqlx.ErrNotFound:
+		return false, nil
+	default:
+		return false, err
+	}
+}
+
+// Update writes the mutable fields of the row. created_at is untouched and
+// updated_at refreshes via the table's ON UPDATE CURRENT_TIMESTAMP.
+func (m *defaultTApplicationCredentialsModel) Update(ctx context.Context, data *TApplicationCredentials) error {
+	query := fmt.Sprintf("update %s set `name` = ?, `api_key` = ?, `api_secret` = ?, `hmac_algo` = ?, `status` = ?, `description` = ?, `updated_by` = ? where `id` = ?",
+		m.table)
+	_, err := m.conn.ExecCtx(ctx, query,
+		data.Name, data.ApiKey, data.ApiSecret, data.HmacAlgo,
+		data.Status, data.Description, data.UpdatedBy, data.Id)
+	return err
+}
+
+// Delete removes the credential row by its id.
+func (m *defaultTApplicationCredentialsModel) Delete(ctx context.Context, id int64) error {
+	query := fmt.Sprintf("delete from %s where `id` = ?", m.table)
+	_, err := m.conn.ExecCtx(ctx, query, id)
+	return err
+}
+
+// FindPage returns one page of rows ordered by id with the total count.
+// status=0 and empty credIden/name mean no filtering; name matches by LIKE.
+func (m *defaultTApplicationCredentialsModel) FindPage(ctx context.Context, page, pageSize int64, status int64, credIden, name string) ([]TApplicationCredentials, int64, error) {
+	var conds []string
+	var args []any
+	if status != 0 {
+		conds = append(conds, "`status` = ?")
+		args = append(args, status)
+	}
+	if credIden != "" {
+		conds = append(conds, "`cred_iden` = ?")
+		args = append(args, credIden)
+	}
+	if keyword, ok := trimName(name); ok {
+		conds = append(conds, "`name` like ? escape '\\\\'")
+		args = append(args, "%"+escapeLike(keyword)+"%")
+	}
+
+	where := ""
+	if len(conds) > 0 {
+		where = " where " + strings.Join(conds, " and ")
+	}
+
+	var total int64
+	if err := m.conn.QueryRowCtx(ctx, &total, fmt.Sprintf("select count(*) from %s%s", m.table, where), args...); err != nil {
+		return nil, 0, err
+	}
+
+	if total == 0 {
+		return nil, 0, nil
+	}
+
+	offset := (page - 1) * pageSize
+	args = append(args, pageSize, offset)
+	query := fmt.Sprintf("select %s from %s%s order by `id` limit ? offset ?", tApplicationCredentialsRows, m.table, where)
+	var resp []TApplicationCredentials
+	if err := m.conn.QueryRowsCtx(ctx, &resp, query, args...); err != nil {
+		return nil, 0, err
+	}
+
+	return resp, total, nil
+}

+ 8 - 5
internal/svc/servicecontext.go

@@ -14,11 +14,12 @@ import (
 )
 )
 
 
 type ServiceContext struct {
 type ServiceContext struct {
-	Config         config.Config
-	DB             sqlx.SqlConn
-	DirectoryModel model.TDirectoryModel
-	KeyModel       model.TKeyModel
-	ServiceModel   model.TServiceModel
+	Config               config.Config
+	DB                   sqlx.SqlConn
+	DirectoryModel       model.TDirectoryModel
+	KeyModel             model.TKeyModel
+	ServiceModel         model.TServiceModel
+	ApplicationCredModel model.TApplicationCredentialsModel
 
 
 	// 审计日志(移植自 sdf-serve)
 	// 审计日志(移植自 sdf-serve)
 	SystemLogModel        model.TSystemLogModel
 	SystemLogModel        model.TSystemLogModel
@@ -52,6 +53,8 @@ func NewServiceContext(c config.Config) *ServiceContext {
 		KeyModel:       model.NewTKeyModel(conn),
 		KeyModel:       model.NewTKeyModel(conn),
 		ServiceModel:   model.NewTServiceModel(conn),
 		ServiceModel:   model.NewTServiceModel(conn),
 
 
+		ApplicationCredModel: model.NewTApplicationCredentialsModel(conn),
+
 		SystemLogModel:        systemLogModel,
 		SystemLogModel:        systemLogModel,
 		ConfigModel:           configModel,
 		ConfigModel:           configModel,
 		LogArchiveRecordModel: model.NewTLogArchiveRecordModel(conn),
 		LogArchiveRecordModel: model.NewTLogArchiveRecordModel(conn),

+ 113 - 62
internal/types/types.go

@@ -3,6 +3,75 @@
 
 
 package types
 package types
 
 
+type ApplicationCredentials struct {
+	Id          int64  `json:"id"`
+	CredIden    string `json:"credIden"`
+	Name        string `json:"name"`
+	ApiKey      string `json:"apiKey"`
+	ApiSecret   string `json:"apiSecret"` // 掩码后的密钥,原文走下发接口
+	Status      int    `json:"status"`    // 启用状态:1.启用;2.禁用
+	Description string `json:"description"`
+	CreatedBy   string `json:"createdBy"`
+	CreatedAt   string `json:"createdAt"`
+	UpdatedBy   string `json:"updatedBy"`
+	UpdatedAt   string `json:"updatedAt"`
+}
+
+type ApplicationCredentialsAddReq struct {
+	Name        string `json:"name"`                 // 应用名称
+	Status      int    `json:"status,optional"`      // 应用状态(1启用,2禁用),缺省启用
+	Description string `json:"description,optional"` // 应用描述
+}
+
+type ApplicationCredentialsAddResp struct {
+	Iden string `json:"iden"` // 应用凭证标识
+}
+
+type ApplicationCredentialsDeleteReq struct {
+	Id int64 `json:"id"`
+}
+
+type ApplicationCredentialsDeleteResp struct {
+	Iden string `json:"iden"` // 应用凭证标识
+}
+
+type ApplicationCredentialsDistributeReq struct {
+	Id int64 `json:"id"`
+}
+
+type ApplicationCredentialsDistributeResp struct {
+	ApiSecret string `json:"apiSecret"` // 应用凭证密钥(明文,仅下发接口返回)
+}
+
+type ApplicationCredentialsResetReq struct {
+	Id int64 `json:"id"`
+}
+
+type ApplicationCredentialsResetResp struct {
+	Iden string `json:"iden"` // 应用凭证标识
+}
+
+type ApplicationCredentialsUpdateStatusReq struct {
+	Id int64 `json:"id"`
+}
+
+type ApplicationCredentialsUpdateStatusResp struct {
+	Iden string `json:"iden"` // 应用凭证标识
+}
+
+type ApplicationListReq struct {
+	Status   int64  `json:"status,optional"`   // 启用状态筛选
+	CredIden string `json:"credIden,optional"` // 应用凭证标识筛选
+	Name     string `json:"name,optional"`     // 应用名称模糊筛选
+	Page     int64  `json:"page,optional"`
+	PageSize int64  `json:"pageSize,optional"`
+}
+
+type ApplicationListResp struct {
+	Total            int64                    `json:"total"`
+	ApplicationLists []ApplicationCredentials `json:"applicationLists"`
+}
+
 type AuditResult struct {
 type AuditResult struct {
 	LogIden string `json:"logIden"`
 	LogIden string `json:"logIden"`
 	Valid   bool   `json:"valid"`
 	Valid   bool   `json:"valid"`
@@ -20,15 +89,14 @@ type CreateKeyReq struct {
 }
 }
 
 
 type CreateServiceReq struct {
 type CreateServiceReq struct {
-	Id      int64  `json:"id,optional"`
-	Name    string `json:"name,optional"`
-	Ip      string `json:"ip,optional"`
-	Port    int64  `json:"port,optional"`
-	Iden    string `json:"iden,optional"`
-	KeyIden string `json:"keyIden,optional"`
-	KeyAlgo string `json:"keyAlgo,optional"`
-	Status  string `json:"status,optional"`
-	// 一键刷库:0=否(默认),1=创建后自动下发刷库(存量明文加密,外挂侧能力就绪后生效)
+	Id          int64    `json:"id,optional"`
+	Name        string   `json:"name,optional"`
+	Ip          string   `json:"ip,optional"`
+	Port        int64    `json:"port,optional"`
+	Iden        string   `json:"iden,optional"`
+	KeyIden     string   `json:"keyIden,optional"`
+	KeyAlgo     string   `json:"keyAlgo,optional"`
+	Status      string   `json:"status,optional"`
 	Brush       int64    `json:"brush,optional"`
 	Brush       int64    `json:"brush,optional"`
 	Directories []string `json:"directories,optional"`
 	Directories []string `json:"directories,optional"`
 }
 }
@@ -40,10 +108,7 @@ type DbPingResponse struct {
 }
 }
 
 
 type DeleteResp struct {
 type DeleteResp struct {
-	Ok bool `json:"ok"`
-
-	// 联动卸载失败等非致命警告(删除本身已成功),前端弹黄色警告;
-	// 目录/密钥删除等其他共用本结构体的接口不填此字段。
+	Ok             bool   `json:"ok"`
 	UnmountWarning string `json:"unmountWarning,optional"`
 	UnmountWarning string `json:"unmountWarning,optional"`
 }
 }
 
 
@@ -84,6 +149,13 @@ type KeyResp struct {
 	CreateAt string `json:"createAt"`
 	CreateAt string `json:"createAt"`
 }
 }
 
 
+type ListDirChildrenReq struct {
+	Ip   string `form:"ip"`   // 目标 Linux 主机 IP
+	Port string `form:"port"` // 目标主机 /api/v1 端口
+	Path string `form:"path"` // 空或 "/" 表示根目录
+	Iden string `form:"iden"` // 接入标识:转发外挂时作为 X-TOKEN 头的值
+}
+
 type ListDirectoryReq struct {
 type ListDirectoryReq struct {
 	Page     int64 `form:"page,optional"`
 	Page     int64 `form:"page,optional"`
 	PageSize int64 `form:"pageSize,optional"`
 	PageSize int64 `form:"pageSize,optional"`
@@ -101,22 +173,6 @@ type ListServiceReq struct {
 	PageSize int64 `form:"pageSize,optional"`
 	PageSize int64 `form:"pageSize,optional"`
 }
 }
 
 
-type Request struct {
-	Name string `path:"name,options=you|me"`
-}
-
-type Response struct {
-	Message string `json:"message"`
-}
-
-// 本地目录懒加载(udeServer 所在 Linux 主机文件系统,只列目录不列文件)
-type ListDirChildrenReq struct {
-	Ip   string `form:"ip"` // 目标 Linux 主机 IP
-	Port string `form:"port"`
-	Path string `form:"path"` // 空或 "/" 表示根目录
-	Iden string `form:"iden"` // 接入标识:转发外挂时作为 X-TOKEN 头的值(外挂侧与环境变量 TOKEN 比对)
-}
-
 type LocalDirNode struct {
 type LocalDirNode struct {
 	Name        string `json:"name"`        // 目录名,如 "usr"
 	Name        string `json:"name"`        // 目录名,如 "usr"
 	Path        string `json:"path"`        // 完整绝对路径,如 "/usr"
 	Path        string `json:"path"`        // 完整绝对路径,如 "/usr"
@@ -182,6 +238,14 @@ type LogRestoreListResp struct {
 	LogRestores []LogRestore `json:"logRestores"`
 	LogRestores []LogRestore `json:"logRestores"`
 }
 }
 
 
+type Request struct {
+	Name string `path:"name,options=you|me"`
+}
+
+type Response struct {
+	Message string `json:"message"`
+}
+
 type ServiceIdReq struct {
 type ServiceIdReq struct {
 	Id int64 `path:"id"`
 	Id int64 `path:"id"`
 }
 }
@@ -192,25 +256,18 @@ type ServiceListResp struct {
 }
 }
 
 
 type ServiceResp struct {
 type ServiceResp struct {
-	Id       int64  `json:"id"`
-	Name     string `json:"name"`
-	Ip       string `json:"ip"`
-	Port     int64  `json:"port"`
-	Iden     string `json:"iden"`
-	KeyIden  string `json:"keyIden"`
-	KeyAlgo  string `json:"keyAlgo"`
-	Status   string `json:"status"`
-	CreateAt string `json:"createAt"`
-
-	// 挂载状态: mounted=已挂载 unmounted=未挂载 failed=挂载失败
-	MountStatus string `json:"mountStatus,optional"`
-
-	// 挂载通知失败等非致命警告(如「服务已创建,但通知外挂程序挂载失败」),
-	// 服务创建本身成功;前端收到后弹黄色警告,不弹红色错误。
+	Id           int64  `json:"id"`
+	Name         string `json:"name"`
+	Ip           string `json:"ip"`
+	Port         int64  `json:"port"`
+	Iden         string `json:"iden"`
+	KeyIden      string `json:"keyIden"`
+	KeyAlgo      string `json:"keyAlgo"`
+	Status       string `json:"status"`
+	CreateAt     string `json:"createAt"`
+	MountStatus  string `json:"mountStatus,optional"`
 	MountWarning string `json:"mountWarning,optional"`
 	MountWarning string `json:"mountWarning,optional"`
-
-	// 一键刷库配置:0=否(默认),1=是
-	Brush int64 `json:"brush"`
+	Brush        int64  `json:"brush"`
 }
 }
 
 
 type SystemLog struct {
 type SystemLog struct {
@@ -313,20 +370,14 @@ type UpdateKeyReq struct {
 }
 }
 
 
 type UpdateServiceReq struct {
 type UpdateServiceReq struct {
-	Id      int64  `path:"id"`
-	Name    string `json:"name,optional"`
-	Ip      string `json:"ip,optional"`
-	Port    int64  `json:"port,optional"`
-	Iden    string `json:"iden,optional"`
-	KeyIden string `json:"keyIden,optional"`
-	KeyAlgo string `json:"keyAlgo,optional"`
-	Status  string `json:"status,optional"`
-
-	// 一键刷库:0=否(默认),1=是。编辑只落库改配置,不追溯触发刷库(刷库编排仅在创建流程)。
-	Brush int64 `json:"brush,optional"`
-
-	// 编辑时全量提交保护目录列表(与新增同款后端收口模式):
-	// nil = 未提交,不动目录;空数组 = 清空目录;非空 = 全量重写。
-	// 落库与挂载联动(卸载被删目录/挂载新目录)由后端在同一接口完成。
+	Id          int64    `path:"id"`
+	Name        string   `json:"name,optional"`
+	Ip          string   `json:"ip,optional"`
+	Port        int64    `json:"port,optional"`
+	Iden        string   `json:"iden,optional"`
+	KeyIden     string   `json:"keyIden,optional"`
+	KeyAlgo     string   `json:"keyAlgo,optional"`
+	Status      string   `json:"status,optional"`
+	Brush       int64    `json:"brush,optional"`
 	Directories []string `json:"directories,optional"`
 	Directories []string `json:"directories,optional"`
 }
 }

+ 126 - 12
udeServer.api

@@ -16,6 +16,9 @@ type DbPingResponse {
 
 
 type DeleteResp {
 type DeleteResp {
 	Ok bool `json:"ok"`
 	Ok bool `json:"ok"`
+	// 联动卸载失败等非致命警告(删除本身已成功),前端弹黄色警告;
+	// 目录/密钥删除等其他共用本结构体的接口不填此字段。
+	UnmountWarning string `json:"unmountWarning,optional"`
 }
 }
 
 
 type CreateDirectoryReq {
 type CreateDirectoryReq {
@@ -85,14 +88,16 @@ type KeyListResp {
 }
 }
 
 
 type CreateServiceReq {
 type CreateServiceReq {
-	Id      int64    `json:"id,optional"`
-	Name    string   `json:"name,optional"`
-	Ip      string   `json:"ip,optional"`
-	Port    int64    `json:"port,optional"`
-	Iden    string   `json:"iden,optional"`
-	KeyIden string   `json:"keyIden,optional"`
-	KeyAlgo string   `json:"keyAlgo,optional"`
-	Status  string   `json:"status,optional"`
+	Id      int64  `json:"id,optional"`
+	Name    string `json:"name,optional"`
+	Ip      string `json:"ip,optional"`
+	Port    int64  `json:"port,optional"`
+	Iden    string `json:"iden,optional"`
+	KeyIden string `json:"keyIden,optional"`
+	KeyAlgo string `json:"keyAlgo,optional"`
+	Status  string `json:"status,optional"`
+	// 一键刷库:0=否(默认),1=创建后自动下发刷库
+	Brush       int64    `json:"brush,optional"`
 	Directories []string `json:"directories,optional"`
 	Directories []string `json:"directories,optional"`
 }
 }
 
 
@@ -105,6 +110,10 @@ type UpdateServiceReq {
 	KeyIden string `json:"keyIden,optional"`
 	KeyIden string `json:"keyIden,optional"`
 	KeyAlgo string `json:"keyAlgo,optional"`
 	KeyAlgo string `json:"keyAlgo,optional"`
 	Status  string `json:"status,optional"`
 	Status  string `json:"status,optional"`
+	// 一键刷库:0=否(默认),1=是。编辑只落库改配置,不追溯触发刷库(刷库编排仅在创建流程)
+	Brush int64 `json:"brush,optional"`
+	// 编辑时全量提交保护目录列表(nil=未提交不动目录;空数组=清空;非空=全量重写)
+	Directories []string `json:"directories,optional"`
 }
 }
 
 
 type ServiceIdReq {
 type ServiceIdReq {
@@ -121,9 +130,12 @@ type ServiceResp {
 	KeyAlgo  string `json:"keyAlgo"`
 	KeyAlgo  string `json:"keyAlgo"`
 	Status   string `json:"status"`
 	Status   string `json:"status"`
 	CreateAt string `json:"createAt"`
 	CreateAt string `json:"createAt"`
-
+	// 挂载状态: mounted=已挂载 unmounted=未挂载 failed=挂载失败
+	MountStatus string `json:"mountStatus,optional"`
 	// 挂载通知失败等非致命警告,前端黄色提示
 	// 挂载通知失败等非致命警告,前端黄色提示
 	MountWarning string `json:"mountWarning,optional"`
 	MountWarning string `json:"mountWarning,optional"`
+	// 一键刷库配置:0=否(默认),1=是
+	Brush int64 `json:"brush"`
 }
 }
 
 
 type ListServiceReq {
 type ListServiceReq {
@@ -141,6 +153,7 @@ type ListDirChildrenReq {
 	Ip   string `form:"ip"` // 目标 Linux 主机 IP
 	Ip   string `form:"ip"` // 目标 Linux 主机 IP
 	Port string `form:"port"` // 目标主机 /api/v1 端口
 	Port string `form:"port"` // 目标主机 /api/v1 端口
 	Path string `form:"path"` // 空或 "/" 表示根目录
 	Path string `form:"path"` // 空或 "/" 表示根目录
+	Iden string `form:"iden"` // 接入标识:转发外挂时作为 X-TOKEN 头的值
 }
 }
 
 
 type LocalDirNode {
 type LocalDirNode {
@@ -308,7 +321,7 @@ type LogRestoreListResp {
 }
 }
 
 
 @server (
 @server (
-	prefix: /api/v1/systemlog
+	prefix: /api/v1/systemLog
 	group:  systemlog
 	group:  systemlog
 )
 )
 // 审计日志管理模块
 // 审计日志管理模块
@@ -335,7 +348,7 @@ service udeServer-api {
 }
 }
 
 
 @server (
 @server (
-	prefix: /api/v1/logarchive
+	prefix: /api/v1/logArchive
 	group:  systemlog
 	group:  systemlog
 )
 )
 // 归档管理模块
 // 归档管理模块
@@ -354,7 +367,7 @@ service udeServer-api {
 }
 }
 
 
 @server (
 @server (
-	prefix: /api/v1/logrestore
+	prefix: /api/v1/logRestore
 	group:  systemlog
 	group:  systemlog
 )
 )
 // 恢复记录模块
 // 恢复记录模块
@@ -426,3 +439,104 @@ service udeServer-api {
 	get /dirs/children (ListDirChildrenReq) returns (DirChildrenResp)
 	get /dirs/children (ListDirChildrenReq) returns (DirChildrenResp)
 }
 }
 
 
+// ===== 应用凭证(移植自 sdf-serve applicationCredentials 模块)=====
+type ApplicationCredentials {
+	Id          int64  `json:"id"`
+	CredIden    string `json:"credIden"`
+	Name        string `json:"name"`
+	ApiKey      string `json:"apiKey"`
+	ApiSecret   string `json:"apiSecret"` // 掩码后的密钥,原文走下发接口
+	Status      int    `json:"status"` // 启用状态:1.启用;2.禁用
+	Description string `json:"description"`
+	CreatedBy   string `json:"createdBy"`
+	CreatedAt   string `json:"createdAt"`
+	UpdatedBy   string `json:"updatedBy"`
+	UpdatedAt   string `json:"updatedAt"`
+}
+
+type ApplicationCredentialsAddReq {
+	Name        string `json:"name"` // 应用名称
+	Status      int    `json:"status,optional"` // 应用状态(1启用,2禁用),缺省启用
+	Description string `json:"description,optional"` // 应用描述
+}
+
+type ApplicationCredentialsAddResp {
+	Iden string `json:"iden"` // 应用凭证标识
+}
+
+type ApplicationCredentialsDeleteReq {
+	Id int64 `json:"id"`
+}
+
+type ApplicationCredentialsDeleteResp {
+	Iden string `json:"iden"` // 应用凭证标识
+}
+
+type ApplicationCredentialsDistributeReq {
+	Id int64 `json:"id"`
+}
+
+type ApplicationCredentialsDistributeResp {
+	ApiSecret string `json:"apiSecret"` // 应用凭证密钥(明文,仅下发接口返回)
+}
+
+type ApplicationCredentialsResetReq {
+	Id int64 `json:"id"`
+}
+
+type ApplicationCredentialsResetResp {
+	Iden string `json:"iden"` // 应用凭证标识
+}
+
+type ApplicationCredentialsUpdateStatusReq {
+	Id int64 `json:"id"`
+}
+
+type ApplicationCredentialsUpdateStatusResp {
+	Iden string `json:"iden"` // 应用凭证标识
+}
+
+type ApplicationListReq {
+	Status   int64  `json:"status,optional"` // 启用状态筛选
+	CredIden string `json:"credIden,optional"` // 应用凭证标识筛选
+	Name     string `json:"name,optional"` // 应用名称模糊筛选
+	Page     int64  `json:"page,optional"`
+	PageSize int64  `json:"pageSize,optional"`
+}
+
+type ApplicationListResp {
+	Total            int64                    `json:"total"`
+	ApplicationLists []ApplicationCredentials `json:"applicationLists"`
+}
+
+@server (
+	prefix: /api/v1/applicationCredentials
+	group:  application
+)
+// 应用凭证管理模块
+service udeServer-api {
+	@doc "添加应用凭证"
+	@handler AddApplicationHandler
+	post /addApplication (ApplicationCredentialsAddReq) returns (ApplicationCredentialsAddResp)
+
+	@doc "应用凭证列表"
+	@handler ApplicationListHandler
+	post /applicationList (ApplicationListReq) returns (ApplicationListResp)
+
+	@doc "删除应用凭证"
+	@handler DeleteApplicationHandler
+	post /deleteApplication (ApplicationCredentialsDeleteReq) returns (ApplicationCredentialsDeleteResp)
+
+	@doc "下发应用凭证密钥"
+	@handler DistributeApiSecretHandler
+	post /distributeApiSecret (ApplicationCredentialsDistributeReq) returns (ApplicationCredentialsDistributeResp)
+
+	@doc "重置应用凭证密钥"
+	@handler ResetApplicationHandler
+	post /resetApplication (ApplicationCredentialsResetReq) returns (ApplicationCredentialsResetResp)
+
+	@doc "切换应用凭证状态(启用/禁用互换)"
+	@handler UpdateApplicationStatusHandler
+	post /updateApplicationStatus (ApplicationCredentialsUpdateStatusReq) returns (ApplicationCredentialsUpdateStatusResp)
+}
+