Browse Source

提交代码

DESKTOP-HN5QP3V\Administrator 3 weeks ago
parent
commit
eab59be1a9

+ 11 - 0
.idea/go.imports.xml

@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="GoImports">
+    <option name="excludedPackages">
+      <array>
+        <option value="github.com/pkg/errors" />
+        <option value="golang.org/x/net/context" />
+      </array>
+    </option>
+  </component>
+</project>

+ 3 - 2
app/api/hotupdate/internal.go

@@ -15,9 +15,10 @@ import (
 	"dsbqj-admin/tool"
 	"dsbqj-admin/tool"
 	"encoding/json"
 	"encoding/json"
 	"fmt"
 	"fmt"
-	"github.com/gin-gonic/gin"
 	"net/http"
 	"net/http"
 	"time"
 	"time"
+
+	"github.com/gin-gonic/gin"
 )
 )
 
 
 const DEF_REQ_TIMEOUT_SEC = 30 * 60
 const DEF_REQ_TIMEOUT_SEC = 30 * 60
@@ -103,7 +104,7 @@ func ChangeStatus(c *gin.Context) {
 		//	appG.Response(http.StatusOK, e.ERROR_UPLOAD_SIGNURL_FAIL, err.Error())
 		//	appG.Response(http.StatusOK, e.ERROR_UPLOAD_SIGNURL_FAIL, err.Error())
 		//	return
 		//	return
 		//}
 		//}
-		var rsp, err = service.GetTHotUpdateVerManager().ChangeStatus(req.ID, req.Status)
+		var rsp, err = service.GetTHotUpdateVerManager().ChangeStatus(req.ID, *req.Status)
 		if err != nil {
 		if err != nil {
 			appG.Response(http.StatusOK, e.NO_RECORD, err.Error())
 			appG.Response(http.StatusOK, e.NO_RECORD, err.Error())
 		} else {
 		} else {

+ 2 - 1
app/api/hotupdate/public.go

@@ -12,8 +12,9 @@ import (
 	"dsbqj-admin/pkg/app"
 	"dsbqj-admin/pkg/app"
 	"dsbqj-admin/pkg/e"
 	"dsbqj-admin/pkg/e"
 	"dsbqj-admin/pkg/logger"
 	"dsbqj-admin/pkg/logger"
-	"github.com/gin-gonic/gin"
 	"net/http"
 	"net/http"
+
+	"github.com/gin-gonic/gin"
 )
 )
 
 
 /*
 /*

+ 71 - 0
app/api/v1/channel.go

@@ -0,0 +1,71 @@
+package v1
+
+import (
+	"dsbqj-admin/app/service"
+	"dsbqj-admin/pkg/app"
+	"dsbqj-admin/pkg/e"
+	"net/http"
+
+	"github.com/gin-gonic/gin"
+)
+
+// ChannelList 渠道列表
+func ChannelList(c *gin.Context) {
+	var appG = app.Gin{C: c}
+	var svc service.ChannelListService
+	if err := c.ShouldBindQuery(&svc); err != nil {
+		appG.Response(http.StatusOK, e.INVALID_PARAMS, err.Error())
+		return
+	}
+	res, err := svc.List()
+	if err != nil {
+		appG.Response(http.StatusOK, e.ERROR, err.Error())
+		return
+	}
+	appG.Response(http.StatusOK, e.SUCCESS, res)
+}
+
+// ChannelCreate 渠道新增
+func ChannelCreate(c *gin.Context) {
+	var appG = app.Gin{C: c}
+	var svc service.ChannelCreateService
+	if err := c.ShouldBindJSON(&svc); err != nil {
+		appG.Response(http.StatusOK, e.INVALID_PARAMS, err.Error())
+		return
+	}
+	if err := svc.Create(); err != nil {
+		appG.Response(http.StatusOK, e.ERROR, err.Error())
+		return
+	}
+	appG.Response(http.StatusOK, e.SUCCESS, nil)
+}
+
+// ChannelUpdate 渠道修改
+func ChannelUpdate(c *gin.Context) {
+	var appG = app.Gin{C: c}
+	var svc service.ChannelUpdateService
+	if err := c.ShouldBindJSON(&svc); err != nil {
+		appG.Response(http.StatusOK, e.INVALID_PARAMS, err.Error())
+		return
+	}
+	if err := svc.Update(); err != nil {
+		appG.Response(http.StatusOK, e.ERROR, err.Error())
+		return
+	}
+	appG.Response(http.StatusOK, e.SUCCESS, nil)
+}
+
+// ChannelDelete 渠道删除
+func ChannelDelete(c *gin.Context) {
+	var appG = app.Gin{C: c}
+	var svc service.ChannelDeleteService
+	if err := c.ShouldBindJSON(&svc); err != nil {
+		appG.Response(http.StatusOK, e.INVALID_PARAMS, err.Error())
+		return
+	}
+	if err := svc.Delete(); err != nil {
+		appG.Response(http.StatusOK, e.ERROR, err.Error())
+		return
+	}
+	appG.Response(http.StatusOK, e.SUCCESS, nil)
+}

+ 10 - 3
app/router/router.go

@@ -4,8 +4,9 @@ import (
 	"dsbqj-admin/app/api/hotupdate"
 	"dsbqj-admin/app/api/hotupdate"
 	v1 "dsbqj-admin/app/api/v1"
 	v1 "dsbqj-admin/app/api/v1"
 	"dsbqj-admin/middleware"
 	"dsbqj-admin/middleware"
-	"github.com/gin-gonic/gin"
 	"os"
 	"os"
+
+	"github.com/gin-gonic/gin"
 )
 )
 
 
 // NewRouter 路由配置
 // NewRouter 路由配置
@@ -33,6 +34,12 @@ func NewRouter() *gin.Engine {
 		webv1.PUT("version", v1.VersionEdit)
 		webv1.PUT("version", v1.VersionEdit)
 		webv1.DELETE("version/:key", v1.VersionDelete)
 		webv1.DELETE("version/:key", v1.VersionDelete)
 		webv1.POST("version/reload", v1.ReloadVersion)
 		webv1.POST("version/reload", v1.ReloadVersion)
+
+		// 渠道管理
+		webv1.GET("channel/list", v1.ChannelList)
+		webv1.POST("channel", v1.ChannelCreate)
+		webv1.PUT("channel", v1.ChannelUpdate)
+		webv1.POST("channel/delete", v1.ChannelDelete)
 	}
 	}
 
 
 	pointv1 := r.Group("/point/v1")
 	pointv1 := r.Group("/point/v1")
@@ -57,10 +64,10 @@ func NewRouter() *gin.Engine {
 	// TODO 内网请求,需要添加ip白名单等
 	// TODO 内网请求,需要添加ip白名单等
 	internalR := r.Group("/internal")
 	internalR := r.Group("/internal")
 	{
 	{
-		internalR.GET("/addversion", hotupdate.AddVersion)
+		internalR.POST("/addversion", hotupdate.AddVersion)
 		internalR.GET("/getmaxverinfo", hotupdate.GetMaxVerInfo)
 		internalR.GET("/getmaxverinfo", hotupdate.GetMaxVerInfo)
 		internalR.GET("/getversionlist", hotupdate.GetVersionList)
 		internalR.GET("/getversionlist", hotupdate.GetVersionList)
-		internalR.GET("/changestatus", hotupdate.ChangeStatus)
+		internalR.PUT("/changestatus", hotupdate.ChangeStatus)
 	}
 	}
 	//webv1.Use(middleware.BodyHandler())
 	//webv1.Use(middleware.BodyHandler())
 	//{
 	//{

+ 141 - 0
app/service/channel.go

@@ -0,0 +1,141 @@
+package service
+
+import (
+	"context"
+	"dsbqj-admin/model/mongo/channel"
+	"dsbqj-admin/pkg/serializer"
+
+	"github.com/kamva/mgm/v3"
+	"go.mongodb.org/mongo-driver/bson"
+	"go.mongodb.org/mongo-driver/bson/primitive"
+	"go.mongodb.org/mongo-driver/mongo/options"
+)
+
+// ChannelListService 渠道列表
+type ChannelListService struct {
+	Name      string `form:"name" json:"name"`
+	PageIndex int    `form:"pageIndex" json:"pageIndex"`
+	PageSize  int    `form:"pageSize" json:"pageSize"`
+}
+
+func (s *ChannelListService) List() (interface{}, error) {
+	if s.PageIndex < 1 {
+		s.PageIndex = 1
+	}
+	if s.PageSize < 1 {
+		s.PageSize = 10
+	}
+
+	filter := bson.M{}
+	if s.Name != "" {
+		filter["$or"] = []bson.M{
+			{"name": bson.M{"$regex": s.Name, "$options": "i"}},
+			{"code": bson.M{"$regex": s.Name, "$options": "i"}},
+		}
+	}
+
+	coll := mgm.Coll(&channel.Channel{})
+
+	// 查询总数
+	total, err := coll.CountDocuments(context.Background(), filter)
+	if err != nil {
+		return nil, err
+	}
+
+	// 分页查询
+	skip := int64((s.PageIndex - 1) * s.PageSize)
+	opts := options.Find().SetSkip(skip).SetLimit(int64(s.PageSize)).SetSort(bson.M{"created_at": -1})
+
+	var list []channel.Channel
+	err = coll.SimpleFindWithCtx(context.Background(), &list, filter, opts)
+	if err != nil {
+		return nil, err
+	}
+
+	return serializer.BuildChannelList(list, int(total)), nil
+}
+
+// ChannelCreateService 渠道新增
+type ChannelCreateService struct {
+	Name        string `json:"name" binding:"required"`
+	Code        string `json:"code" binding:"required"`
+	Description string `json:"description"`
+	Status      *bool  `json:"status"`
+}
+
+func (s *ChannelCreateService) Create() error {
+	c := &channel.Channel{
+		Name:        s.Name,
+		Code:        s.Code,
+		Description: s.Description,
+		Status:      true,
+	}
+	if s.Status != nil {
+		c.Status = *s.Status
+	}
+	return mgm.Coll(c).Create(c)
+}
+
+// ChannelUpdateService 渠道修改
+type ChannelUpdateService struct {
+	ID          string  `json:"id" binding:"required"`
+	Name        *string `json:"name"`
+	Code        *string `json:"code"`
+	Description *string `json:"description"`
+	Status      *bool   `json:"status"`
+}
+
+func (s *ChannelUpdateService) Update() error {
+	objectID, err := primitive.ObjectIDFromHex(s.ID)
+	if err != nil {
+		return err
+	}
+
+	updateDoc := bson.M{}
+	if s.Name != nil {
+		updateDoc["name"] = *s.Name
+	}
+	if s.Code != nil {
+		updateDoc["code"] = *s.Code
+	}
+	if s.Description != nil {
+		updateDoc["description"] = *s.Description
+	}
+	if s.Status != nil {
+		updateDoc["status"] = *s.Status
+	}
+	if len(updateDoc) == 0 {
+		return nil
+	}
+
+	_, err = mgm.Coll(&channel.Channel{}).UpdateOne(
+		context.Background(),
+		bson.M{"_id": objectID},
+		bson.M{"$set": updateDoc},
+	)
+	return err
+}
+
+// ChannelDeleteService 渠道删除
+type ChannelDeleteService struct {
+	IDs []string `json:"ids" binding:"required"`
+}
+
+func (s *ChannelDeleteService) Delete() error {
+	objectIDs := make([]primitive.ObjectID, 0, len(s.IDs))
+	for _, id := range s.IDs {
+		oid, err := primitive.ObjectIDFromHex(id)
+		if err != nil {
+			continue
+		}
+		objectIDs = append(objectIDs, oid)
+	}
+	if len(objectIDs) == 0 {
+		return nil
+	}
+	_, err := mgm.Coll(&channel.Channel{}).DeleteMany(
+		context.Background(),
+		bson.M{"_id": bson.M{"$in": objectIDs}},
+	)
+	return err
+}

+ 7 - 5
app/service/hotupdate.go

@@ -12,11 +12,12 @@ import (
 	"dsbqj-admin/pkg/logger"
 	"dsbqj-admin/pkg/logger"
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
-	"go.mongodb.org/mongo-driver/bson"
-	"go.mongodb.org/mongo-driver/mongo/options"
 	"strconv"
 	"strconv"
 	"strings"
 	"strings"
 	"sync"
 	"sync"
+
+	"go.mongodb.org/mongo-driver/bson"
+	"go.mongodb.org/mongo-driver/mongo/options"
 )
 )
 
 
 var validProj = map[string]bool{
 var validProj = map[string]bool{
@@ -135,11 +136,12 @@ func (this *THotUpdateVerManager) ChangeStatus(id string, status int16) (*TGetVe
 	}
 	}
 	logger.Info("after update mVersion", mVersion)
 	logger.Info("after update mVersion", mVersion)
 	err = this.reloadPubVerBy(mVersion.Proj, mVersion.Os)
 	err = this.reloadPubVerBy(mVersion.Proj, mVersion.Os)
+	vRsp := &TGetVersionRsp{}
 	if err != nil {
 	if err != nil {
-		return nil, fmt.Errorf("reloadPubVerBy error: %v", err)
+		logger.Warn(fmt.Errorf("reloadPubVerBy error: %v", err).Error())
+	} else {
+		vRsp.FromMVersion(mVersion)
 	}
 	}
-	vRsp := &TGetVersionRsp{}
-	vRsp.FromMVersion(mVersion)
 	return vRsp, nil
 	return vRsp, nil
 }
 }
 
 

+ 1 - 1
app/service/type.go

@@ -62,5 +62,5 @@ type TGetVersionListReq struct {
 
 
 type TChangeStautsReq struct {
 type TChangeStautsReq struct {
 	ID     string `form:"id" binding:"required" json:"id"`
 	ID     string `form:"id" binding:"required" json:"id"`
-	Status int16  `form:"status" binding:"required" json:"status"`
+	Status *int16 `form:"status" binding:"required" json:"status"`
 }
 }

+ 18 - 3
app/service/version.go

@@ -4,6 +4,7 @@ import (
 	"context"
 	"context"
 	"dsbqj-admin/app/task"
 	"dsbqj-admin/app/task"
 	"dsbqj-admin/model/mongo/version"
 	"dsbqj-admin/model/mongo/version"
+
 	"github.com/kamva/mgm/v3"
 	"github.com/kamva/mgm/v3"
 	"go.mongodb.org/mongo-driver/bson"
 	"go.mongodb.org/mongo-driver/bson"
 	"go.mongodb.org/mongo-driver/bson/primitive"
 	"go.mongodb.org/mongo-driver/bson/primitive"
@@ -18,31 +19,40 @@ func (service *VersionReloadService) Reload() error {
 }
 }
 
 
 type VersionServerService struct {
 type VersionServerService struct {
+	Channel string `json:"channel" binding:"required"`
 }
 }
 
 
 func (service *VersionServerService) Servers() ([]*version.Version, error) {
 func (service *VersionServerService) Servers() ([]*version.Version, error) {
-	return task.VersionTask.Servers(), nil
+	return task.VersionTask.Servers(service.Channel), nil
 }
 }
 
 
 type VersionCheckService struct {
 type VersionCheckService struct {
 	Version string `json:"version" binding:"required"`
 	Version string `json:"version" binding:"required"`
+	Channel string `json:"channel" binding:"required"`
 }
 }
 
 
 func (service *VersionCheckService) Check() (*version.Version, error) {
 func (service *VersionCheckService) Check() (*version.Version, error) {
-	return task.VersionTask.Check(service.Version), nil
+	return task.VersionTask.Check(service.Version, service.Channel), nil
 }
 }
 
 
 type VersionShowService struct {
 type VersionShowService struct {
 	CondPage
 	CondPage
+	Channel string `form:"channel" json:"channel"`
 }
 }
 
 
 func (service *VersionShowService) List() ([]version.Version, error) {
 func (service *VersionShowService) List() ([]version.Version, error) {
 	versions := make([]version.Version, 0)
 	versions := make([]version.Version, 0)
-	err := mgm.Coll(&version.Version{}).SimpleFind(&versions, bson.M{})
+	var queryDoc = bson.M{}
+	if service.Channel != "" {
+		queryDoc["channel"] = service.Channel
+	}
+
+	err := mgm.Coll(&version.Version{}).SimpleFind(&versions, queryDoc)
 	return versions, err
 	return versions, err
 }
 }
 
 
 type VersionCreateService struct {
 type VersionCreateService struct {
+	Channel string `json:"channel" binding:"required"`
 	Version string `json:"version" binding:"required"`
 	Version string `json:"version" binding:"required"`
 	Name    string `json:"name" binding:"required"`
 	Name    string `json:"name" binding:"required"`
 	CDN     string `json:"cdn" binding:"required"`
 	CDN     string `json:"cdn" binding:"required"`
@@ -51,6 +61,7 @@ type VersionCreateService struct {
 
 
 func (service *VersionCreateService) Create() error {
 func (service *VersionCreateService) Create() error {
 	version := new(version.Version)
 	version := new(version.Version)
+	version.Channel = service.Channel
 	version.Name = service.Name
 	version.Name = service.Name
 	version.Version = service.Version
 	version.Version = service.Version
 	version.CDN = service.CDN
 	version.CDN = service.CDN
@@ -61,6 +72,7 @@ func (service *VersionCreateService) Create() error {
 
 
 type VersionEditService struct {
 type VersionEditService struct {
 	Id      string  `json:"id" binding:"required"`
 	Id      string  `json:"id" binding:"required"`
+	Channel *string `json:"channel"`
 	Version *string `json:"version"`
 	Version *string `json:"version"`
 	Name    *string `json:"name"`
 	Name    *string `json:"name"`
 	CDN     *string `json:"cdn"`
 	CDN     *string `json:"cdn"`
@@ -74,6 +86,9 @@ func (service *VersionEditService) Edit() error {
 	objectId, _ := primitive.ObjectIDFromHex(service.Id)
 	objectId, _ := primitive.ObjectIDFromHex(service.Id)
 	filterDoc := bson.M{"_id": objectId}
 	filterDoc := bson.M{"_id": objectId}
 	updateDoc := bson.M{}
 	updateDoc := bson.M{}
+	if service.Channel != nil {
+		updateDoc["channel"] = service.Channel
+	}
 	if service.Name != nil {
 	if service.Name != nil {
 		updateDoc["name"] = service.Name
 		updateDoc["name"] = service.Name
 	}
 	}

+ 13 - 5
app/task/version.go

@@ -3,9 +3,10 @@ package task
 import (
 import (
 	"dsbqj-admin/model/mongo/version"
 	"dsbqj-admin/model/mongo/version"
 	"dsbqj-admin/pkg/util"
 	"dsbqj-admin/pkg/util"
+	"sync"
+
 	"github.com/kamva/mgm/v3"
 	"github.com/kamva/mgm/v3"
 	"go.mongodb.org/mongo-driver/bson"
 	"go.mongodb.org/mongo-driver/bson"
-	"sync"
 )
 )
 
 
 var VersionTask *Versions
 var VersionTask *Versions
@@ -35,11 +36,11 @@ func (this *Versions) Exec() {
 	}
 	}
 }
 }
 
 
-func (this *Versions) Check(version string) *version.Version {
+func (this *Versions) Check(version string, channel string) *version.Version {
 	data, ok := this.VMap.Get(version)
 	data, ok := this.VMap.Get(version)
 	if !ok { // 遍历列表
 	if !ok { // 遍历列表
 		for _, v := range this.VList.View() {
 		for _, v := range this.VList.View() {
-			if v.Default {
+			if v.Default && v.Channel == channel {
 				data = v
 				data = v
 				break
 				break
 			}
 			}
@@ -49,6 +50,13 @@ func (this *Versions) Check(version string) *version.Version {
 	return data
 	return data
 }
 }
 
 
-func (this *Versions) Servers() []*version.Version {
-	return this.VList.View()
+func (this *Versions) Servers(channel string) []*version.Version {
+	var res = make([]*version.Version, 0)
+	for _, v := range this.VList.View() {
+		if v.Channel == channel {
+			res = append(res, v)
+		}
+	}
+	  
+	return res
 }
 }

+ 29 - 0
model/mongo/channel/channel.go

@@ -0,0 +1,29 @@
+package channel
+
+import (
+	"time"
+
+	"github.com/kamva/mgm/v3"
+)
+
+// Channel 渠道模型
+type Channel struct {
+	mgm.DefaultModel `bson:",inline"`
+	Name             string `bson:"name" json:"name"`
+	Code             string `bson:"code" json:"code"`
+	Description      string `bson:"description" json:"description"`
+	Status           bool   `bson:"status" json:"status"`
+}
+
+// CollectionName 指定 MongoDB 集合名
+func (Channel) CollectionName() string {
+	return "channels"
+}
+
+// GetCreateTime 返回格式化的创建时间,供前端使用
+func (c *Channel) GetCreateTime() string {
+	if c.CreatedAt.IsZero() {
+		return ""
+	}
+	return c.CreatedAt.In(time.Local).Format("2006-01-02 15:04:05")
+}

+ 1 - 0
model/mongo/version/version.go

@@ -6,6 +6,7 @@ import (
 
 
 type Version struct {
 type Version struct {
 	mgm.DefaultModel `bson:",inline" json:"id"`
 	mgm.DefaultModel `bson:",inline" json:"id"`
+	Channel          string `bson:"channel" json:"channel"`
 	Version          string `bson:"version" json:"version"`
 	Version          string `bson:"version" json:"version"`
 	Name             string `bson:"name" json:"name"`
 	Name             string `bson:"name" json:"name"`
 	CDN              string `bson:"cdn" json:"cdn"`
 	CDN              string `bson:"cdn" json:"cdn"`

+ 2 - 3
pkg/app/response.go

@@ -15,12 +15,11 @@ type Response struct {
 }
 }
 
 
 func encode(data interface{}) interface{} {
 func encode(data interface{}) interface{} {
-	if os.Getenv("NAME") == "admin" {
+	if os.Getenv("NAME") == "admin" && os.Getenv("DISABLE_RESPONSE_ENCRYPT") != "true" {
 		buf, _ := json.Marshal(data)
 		buf, _ := json.Marshal(data)
 		return util.EncryptDES_ECB(buf, os.Getenv("CRYPRO_SECRET"))
 		return util.EncryptDES_ECB(buf, os.Getenv("CRYPRO_SECRET"))
-	} else {
-		return data
 	}
 	}
+	return data
 }
 }
 
 
 // Response setting gin.JSON
 // Response setting gin.JSON

+ 5 - 4
pkg/helper/wechat/wechatHelper.go

@@ -8,9 +8,6 @@ import (
 	"dsbqj-admin/pkg/util"
 	"dsbqj-admin/pkg/util"
 	"encoding/hex"
 	"encoding/hex"
 	"fmt"
 	"fmt"
-	"github.com/goccy/go-json"
-	"github.com/silenceper/wechat"
-	wechatCtx "github.com/silenceper/wechat/context"
 	"os"
 	"os"
 	"sort"
 	"sort"
 	"strconv"
 	"strconv"
@@ -18,6 +15,10 @@ import (
 	"sync/atomic"
 	"sync/atomic"
 	"time"
 	"time"
 
 
+	"github.com/goccy/go-json"
+	"github.com/silenceper/wechat"
+	wechatCtx "github.com/silenceper/wechat/context"
+
 	wechatCache "dsbqj-admin/pkg/cache/wechat"
 	wechatCache "dsbqj-admin/pkg/cache/wechat"
 )
 )
 
 
@@ -203,7 +204,7 @@ func (this *WechatHelper) SendWechatSubscribe(openid string, template string, ms
 	}
 	}
 	if re.Errcode != 0 {
 	if re.Errcode != 0 {
 		if re.Errcode == 40003 || re.Errcode == 43101 { //openid无效,appid与openid不匹配, 用户未订阅消息(用户未在近两小时访问小程序)
 		if re.Errcode == 40003 || re.Errcode == 43101 { //openid无效,appid与openid不匹配, 用户未订阅消息(用户未在近两小时访问小程序)
-			logger.Info("CheckWechatMsg msg: %s ret: %d errmsg: %s", msg, re.Errcode, re.Errmsg)
+			logger.Info("CheckWechatMsg msg: %s ret: addversion%d errmsg: %s", msg, re.Errcode, re.Errmsg)
 			return 0
 			return 0
 		}
 		}
 
 

+ 42 - 0
pkg/serializer/channel.go

@@ -0,0 +1,42 @@
+package serializer
+
+import (
+	"dsbqj-admin/model/mongo/channel"
+)
+
+// ChannelResponse 渠道响应,匹配前端 ChannelData 格式
+type ChannelResponse struct {
+	ID          string `json:"id"`
+	Name        string `json:"name"`
+	Code        string `json:"code"`
+	Description string `json:"description"`
+	Status      bool   `json:"status"`
+	CreateTime  string `json:"createTime"`
+}
+
+// BuildChannel 构建单条渠道响应
+func BuildChannel(c *channel.Channel) *ChannelResponse {
+	if c == nil {
+		return nil
+	}
+	return &ChannelResponse{
+		ID:          c.ID.Hex(),
+		Name:        c.Name,
+		Code:        c.Code,
+		Description: c.Description,
+		Status:      c.Status,
+		CreateTime:  c.GetCreateTime(),
+	}
+}
+
+// BuildChannelList 构建渠道列表响应
+func BuildChannelList(list []channel.Channel, total int) map[string]interface{} {
+	items := make([]*ChannelResponse, 0, len(list))
+	for i := range list {
+		items = append(items, BuildChannel(&list[i]))
+	}
+	return map[string]interface{}{
+		"list":  items,
+		"total": total,
+	}
+}