package handler import ( "crypto/rand" "encoding/hex" "kenaito-conf/server/internal/dao" "kenaito-conf/server/internal/middleware" "kenaito-conf/server/internal/model" "net/http" "strconv" "strings" "time" "github.com/gin-gonic/gin" ) type AppHandler struct{} func NewAppHandler() *AppHandler { return &AppHandler{} } type AppListRequest struct { PageNo int `form:"pageNo" binding:"required"` PageSize int `form:"pageSize" binding:"required"` AppType string `form:"appType"` Name string `form:"name"` Code string `form:"code"` GroupId string `form:"groupId"` } func (h *AppHandler) List(c *gin.Context) { var req AppListRequest if err := c.ShouldBindQuery(&req); err != nil { c.JSON(http.StatusBadRequest, model.WrapperResponse{Code: 400, Msg: "参数错误"}) return } userInfo := middleware.GetUserInfo(c) query := dao.Engine.Where("enabled = 1") if !userInfo.IsSuperAdmin() { userId, _ := strconv.Atoi(userInfo.Id) userIdStr := strconv.Itoa(userId) if userInfo.IsGroupAdmin() && userInfo.GroupId != nil { // 组管理员:本组应用 + 自己是负责人的应用 query = query.And("(group_id = ? OR FIND_IN_SET('" + userIdStr + "', owner_ids) > 0 OR id IN (SELECT app_id FROM user_permission WHERE user_id = ?))", *userInfo.GroupId, userId) } else { // 普通用户:自己是负责人的应用 + 被授权的应用 query = query.And("(FIND_IN_SET('" + userIdStr + "', owner_ids) > 0 OR id IN (SELECT app_id FROM user_permission WHERE user_id = ?))", userId) } } var apps []model.Application whereClause := "enabled = 1" if !userInfo.IsSuperAdmin() { userId, _ := strconv.Atoi(userInfo.Id) userIdStr := strconv.Itoa(userId) if userInfo.IsGroupAdmin() && userInfo.GroupId != nil { // 组管理员:本组应用 + 自己是负责人的应用 whereClause += " AND (group_id = " + strconv.Itoa(*userInfo.GroupId) + " OR FIND_IN_SET('" + userIdStr + "', owner_ids) > 0 OR id IN (SELECT app_id FROM user_permission WHERE user_id = " + strconv.Itoa(userId) + "))" } else { // 普通用户:自己是负责人的应用 + 被授权的应用 whereClause += " AND (FIND_IN_SET('" + userIdStr + "', owner_ids) > 0 OR id IN (SELECT app_id FROM user_permission WHERE user_id = " + strconv.Itoa(userId) + "))" } } if req.AppType != "" { whereClause += " AND service_id = '" + req.AppType + "'" } if req.Name != "" { whereClause += " AND name LIKE '%" + req.Name + "%'" } if req.Code != "" { whereClause += " AND code LIKE '%" + req.Code + "%'" } if req.GroupId != "" { whereClause += " AND group_id = " + req.GroupId } total, _ := dao.Engine.Where(whereClause).Count(&model.Application{}) dao.Engine.Where(whereClause).Limit(req.PageSize, (req.PageNo-1)*req.PageSize).Find(&apps) // load group names - batch query to avoid N+1 groupIds := make(map[int]bool) for _, app := range apps { if app.GroupId != nil { groupIds[*app.GroupId] = true } } groupMap := make(map[int]string) if len(groupIds) > 0 { ids := make([]int, 0, len(groupIds)) for gid := range groupIds { ids = append(ids, gid) } var groups []model.BusinessGroup dao.Engine.In("id", ids).Find(&groups) for _, g := range groups { groupMap[g.Id] = g.Name } } for i := range apps { if apps[i].GroupId != nil { apps[i].GroupName = groupMap[*apps[i].GroupId] } } c.JSON(http.StatusOK, model.PageResult{PageNo: req.PageNo, PageSize: req.PageSize, Total: total, Data: apps}) } func (h *AppHandler) GetById(c *gin.Context) { id, _ := strconv.Atoi(c.Param("id")) var app model.Application has, _ := dao.Engine.ID(id).Get(&app) if !has { c.JSON(http.StatusOK, model.WrapperResponse{Code: 404, Msg: "应用不存在"}) return } c.JSON(http.StatusOK, model.WrapperResponse{Code: 0, Data: app}) } type AddAppRequest struct { Id int `json:"id"` GroupId *int `json:"groupId"` Name string `json:"name" binding:"required"` Code string `json:"code" binding:"required"` ServiceId string `json:"serviceId"` OwnerIds []int `json:"ownerIds"` Remarks string `json:"remarks"` } func (h *AppHandler) Add(c *gin.Context) { var req AddAppRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, model.WrapperResponse{Code: 400, Msg: "参数错误"}) return } userInfo := middleware.GetUserInfo(c) // check if code exists var existing model.Application if has, _ := dao.Engine.Where("code = ?", req.Code).Get(&existing); has { c.JSON(http.StatusOK, model.WrapperResponse{Code: 1002, Msg: "应用已存在"}) return } // 处理负责人:如果未指定,默认为当前用户 ownerIds := req.OwnerIds if len(ownerIds) == 0 { uid, _ := strconv.Atoi(userInfo.Id) ownerIds = []int{uid} } // 验证所有用户ID都存在 var users []model.User if err := dao.Engine.In("id", ownerIds).Find(&users); err != nil || len(users) != len(ownerIds) { c.JSON(http.StatusOK, model.WrapperResponse{Code: 400, Msg: "部分负责人不存在"}) return } // 构建逗号分隔的用户名和ID字符串 ownerNames := make([]string, 0, len(users)) ownerIdStrs := make([]string, 0, len(users)) for _, u := range users { ownerNames = append(ownerNames, u.Name) ownerIdStrs = append(ownerIdStrs, strconv.Itoa(u.Id)) } app := model.Application{ GroupId: req.GroupId, Name: req.Name, Code: req.Code, ServiceId: req.ServiceId, OwnerIds: strings.Join(ownerIdStrs, ","), OwnerNames: strings.Join(ownerNames, ","), Remarks: req.Remarks, Enabled: true, CreatedAt: func() *time.Time { t := time.Now(); return &t }(), CreatedBy: userInfo.Name, } dao.Engine.Insert(&app) c.JSON(http.StatusOK, model.WrapperResponse{Code: 0, Data: true}) } func (h *AppHandler) Update(c *gin.Context) { var req AddAppRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, model.WrapperResponse{Code: 400, Msg: "参数错误"}) return } if req.Id == 0 { c.JSON(http.StatusOK, model.WrapperResponse{Code: 400, Msg: "id参数缺失"}) return } var app model.Application has, _ := dao.Engine.ID(req.Id).Get(&app) if !has { c.JSON(http.StatusOK, model.WrapperResponse{Code: 404, Msg: "应用不存在"}) return } userInfo := middleware.GetUserInfo(c) app.Name = req.Name app.ServiceId = req.ServiceId app.Remarks = req.Remarks // 更新负责人 if len(req.OwnerIds) > 0 { // 验证所有用户ID都存在 var users []model.User if err := dao.Engine.In("id", req.OwnerIds).Find(&users); err != nil || len(users) != len(req.OwnerIds) { c.JSON(http.StatusOK, model.WrapperResponse{Code: 400, Msg: "部分负责人不存在"}) return } // 构建逗号分隔的用户名和ID字符串 ownerNames := make([]string, 0, len(users)) ownerIdStrs := make([]string, 0, len(users)) for _, u := range users { ownerNames = append(ownerNames, u.Name) ownerIdStrs = append(ownerIdStrs, strconv.Itoa(u.Id)) } app.OwnerIds = strings.Join(ownerIdStrs, ",") app.OwnerNames = strings.Join(ownerNames, ",") } app.UpdatedBy = userInfo.Name now := time.Now() app.UpdatedAt = &now dao.Engine.ID(app.Id).Cols("name", "service_id", "remarks", "owner_ids", "owner_names", "updated_by", "updated_at").Update(&app) c.JSON(http.StatusOK, model.WrapperResponse{Code: 0, Data: true}) } func (h *AppHandler) Delete(c *gin.Context) { id, _ := strconv.Atoi(c.Param("id")) userInfo := middleware.GetUserInfo(c) if !userInfo.IsSuperAdmin() { c.JSON(http.StatusForbidden, model.WrapperResponse{Code: 403, Msg: "超级管理员才能删除"}) return } var app model.Application has, _ := dao.Engine.ID(id).Get(&app) if !has { c.JSON(http.StatusOK, model.WrapperResponse{Code: 404, Msg: "应用不存在"}) return } app.Enabled = false app.UpdatedBy = userInfo.Name now := time.Now() app.UpdatedAt = &now dao.Engine.ID(app.Id).Cols("enabled", "updated_by", "updated_at").Update(&app) // soft delete related configs dao.Engine.Where("app_id = ?", id).Cols("enabled").Update(&model.AppConfig{Enabled: false}) c.JSON(http.StatusOK, model.WrapperResponse{Code: 0}) } func (h *AppHandler) Switch(c *gin.Context) { id, _ := strconv.Atoi(c.Param("id")) userInfo := middleware.GetUserInfo(c) var app model.Application has, _ := dao.Engine.ID(id).Get(&app) if !has { c.JSON(http.StatusOK, model.WrapperResponse{Code: 404, Msg: "应用不存在"}) return } app.Enabled = !app.Enabled app.UpdatedBy = userInfo.Name now := time.Now() app.UpdatedAt = &now dao.Engine.ID(app.Id).Cols("enabled", "updated_by", "updated_at").Update(&app) c.JSON(http.StatusOK, model.WrapperResponse{Code: 0, Data: true}) } func (h *AppHandler) Options(c *gin.Context) { userInfo := middleware.GetUserInfo(c) query := dao.Engine.Where("enabled = 1") if !userInfo.IsSuperAdmin() { if userInfo.IsGroupAdmin() && userInfo.GroupId != nil { query = query.And("group_id = ?", *userInfo.GroupId) } else { userId, _ := strconv.Atoi(userInfo.Id) userIdStr := strconv.Itoa(userId) query = query.And("FIND_IN_SET(?, owner_ids) > 0", userIdStr) } } var apps []model.Application query.Find(&apps) options := make([]model.SelectOption, 0) for _, app := range apps { options = append(options, model.SelectOption{Value: strconv.Itoa(app.Id), Label: app.Name}) } c.JSON(http.StatusOK, model.WrapperResponse{Code: 0, Data: options}) } func (h *AppHandler) GenerateToken(c *gin.Context) { id, _ := strconv.Atoi(c.Param("id")) userInfo := middleware.GetUserInfo(c) var app model.Application has, _ := dao.Engine.ID(id).Get(&app) if !has { c.JSON(http.StatusOK, model.WrapperResponse{Code: 404, Msg: "应用不存在"}) return } bytes := make([]byte, 32) if _, err := rand.Read(bytes); err != nil { c.JSON(http.StatusOK, model.WrapperResponse{Code: 500, Msg: "生成 Token 失败"}) return } token := hex.EncodeToString(bytes) app.Token = token app.UpdatedBy = userInfo.Name now := time.Now() app.UpdatedAt = &now dao.Engine.ID(app.Id).Cols("token", "updated_by", "updated_at").Update(&app) c.JSON(http.StatusOK, model.WrapperResponse{Code: 0, Data: token}) } func (h *AppHandler) DeleteToken(c *gin.Context) { id, _ := strconv.Atoi(c.Param("id")) userInfo := middleware.GetUserInfo(c) var app model.Application has, _ := dao.Engine.ID(id).Get(&app) if !has { c.JSON(http.StatusOK, model.WrapperResponse{Code: 404, Msg: "应用不存在"}) return } app.Token = "" app.UpdatedBy = userInfo.Name now := time.Now() app.UpdatedAt = &now dao.Engine.ID(app.Id).Cols("token", "updated_by", "updated_at").Update(&app) c.JSON(http.StatusOK, model.WrapperResponse{Code: 0, Data: true}) }