refactor: 后端仓库层收口(A5:技能/应用定义 + 用户应用中心)
D25 定的对象层三类一级对象(专员 / 技能 / 应用),A4 收了专员,这次是 技能与应用。原先是 handler 里直接 store.DB.Create/Save/Delete,现在统一走 repository,与 A1~A4 同一形状:仓库方法返回 bool,三态参数用 *bool 表达。 新增: - repository.SkillDefinitionRepo / XAppDefinitionRepo —— List / GetByID / GetByKey / Insert / Update / Delete - repository.UserXAppCenterRepo —— 收藏/最近使用/自定义应用,每人一份 两处刻意偏离既有惯例,都写了理由在代码里: 1. List(state string, exposedToUser *bool) 不在仓库里定默认值。「不传 state 就只看 active」是列表接口的契约,解析 query 参数是 handler 的活;仓库只 执行过滤。exposedToUser 用 *bool 而非 bool,因为「不传该参数」与「传 false」语义不同——前者要全量,压成 bool 会把不传当成 false。 2. UserXAppCenterRepo.FindByUser 返回 (row, error) 而不是 bool。「没找到」在 这个仓库是有意义的第三态:表示该用户还没配过、调用方要新建一份。若压成 bool,一旦读取真出错(SQLite 本地锁等待是常事)就会被当成「没配过」,转而 写一份空白配置,把用户已有的收藏和最近使用抹掉。 技能与应用的读接口共用同一个仓库变量,只声明一处(skillDefinitionRepo 留在 admin_handlers.go)——两份变量持有同一仓库时,测试里覆写一份、另一份照旧, 行为会静默分叉。 A5 范围内的 store.DB 直用已清零;skills/api/office_handlers.go 里仍有 3 处, 是 store.DB.Transaction,QueryBuilder 不带事务,留着别动。 验证(tmp_vfy_a5,临时程序,验完已删):真实路由 + 真实 HTTP,跑在数据库 副本上。55 条断言全绿,覆盖技能/应用的列表三态、按 key 取、增改删、404 路径、 用户应用中心的空配置/覆盖写/多用户隔离。 另有 14 条变异测试确认断言真的会红(13 捕获 / 1 设计上不可观测),其中 排序断言原本是假的:三条种子的插入顺序恰好也是 sort_order 升序,删掉 ORDER BY 照样绿。补了一条「sort_order 更小但插入更晚」的记录才透光。 Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,69 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
skillmodel "eai_agentplatform/backend/internal/skills/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SkillDefinition 技能定义仓库。
|
||||||
|
//
|
||||||
|
// 技能是对象层三类一级对象之一(专员 / 技能 / 应用),与专员目录一样,
|
||||||
|
// 「按 key 取技能」原先在多个文件里各写一遍,统一收这里。
|
||||||
|
type SkillDefinitionRepo struct{ *QueryBuilder }
|
||||||
|
|
||||||
|
// List 技能定义列表(sort_order ASC, id ASC)。
|
||||||
|
//
|
||||||
|
// state 不在这里定默认值:「不传 state 就只看 active」是列表接口的契约,
|
||||||
|
// 由 handler 解析 query 参数后把结果传进来(与 ActionDefinitionRepo.List 同一约定)。
|
||||||
|
//
|
||||||
|
// exposedToUser 是三态:nil 表示不按该列过滤,非 nil 按值精确匹配。
|
||||||
|
// 不用 bool 是因为「不传该参数」与「传 false」语义不同,前者要全量。
|
||||||
|
func (r SkillDefinitionRepo) List(state string, exposedToUser *bool) []skillmodel.SkillDefinition {
|
||||||
|
q := r.Type(&skillmodel.SkillDefinition{})
|
||||||
|
if state != "" {
|
||||||
|
q = q.Where("state = ?", state)
|
||||||
|
}
|
||||||
|
if exposedToUser != nil {
|
||||||
|
q = q.Where("exposed_to_user = ?", *exposedToUser)
|
||||||
|
}
|
||||||
|
var items []skillmodel.SkillDefinition
|
||||||
|
if q.Order("sort_order ASC, id ASC").Find(&items) {
|
||||||
|
return items
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetByID 按 ID 取。
|
||||||
|
func (r SkillDefinitionRepo) GetByID(id uint) (skillmodel.SkillDefinition, bool) {
|
||||||
|
var s skillmodel.SkillDefinition
|
||||||
|
if r.Type(&s).Where("id = ?", id).First(&s) {
|
||||||
|
return s, true
|
||||||
|
}
|
||||||
|
return skillmodel.SkillDefinition{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetByKey 按 key 取(key 上有唯一索引)。
|
||||||
|
func (r SkillDefinitionRepo) GetByKey(key string) (skillmodel.SkillDefinition, bool) {
|
||||||
|
var s skillmodel.SkillDefinition
|
||||||
|
if r.Type(&s).Where("key = ?", key).First(&s) {
|
||||||
|
return s, true
|
||||||
|
}
|
||||||
|
return skillmodel.SkillDefinition{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert 新建技能定义。
|
||||||
|
func (r SkillDefinitionRepo) Insert(s *skillmodel.SkillDefinition) bool {
|
||||||
|
return r.QueryBuilder.Insert(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update 更新技能定义。
|
||||||
|
func (r SkillDefinitionRepo) Update(s *skillmodel.SkillDefinition) bool {
|
||||||
|
return r.Save(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete 硬删除技能定义。
|
||||||
|
//
|
||||||
|
// 表上没有软删字段,删了就是删了 —— 跟「停用」(state=inactive)是两回事:
|
||||||
|
// 停用还留着记录,挂在它名下的技能引用仍能解释。
|
||||||
|
func (r SkillDefinitionRepo) Delete(s *skillmodel.SkillDefinition) bool {
|
||||||
|
return r.QueryBuilder.Delete(s)
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
|
|
||||||
|
xappmodel "eai_agentplatform/backend/internal/xapps/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UserXAppCenter 用户自己的应用中心状态仓库(收藏 / 最近使用 / 自定义应用,每人一份)。
|
||||||
|
//
|
||||||
|
// 这里的方法签名**有意不跟**其它仓库的 bool 惯例:FindByUser 返回 error。
|
||||||
|
// 原因是本仓库的「没找到」是一个有意义的第三态 —— 它表示「该用户还没配过,
|
||||||
|
// 调用方要新建一份」。若压成 bool,一旦读取真出错(SQLite 本地锁等待是常事)
|
||||||
|
// 就会被当成「没配过」,转而写一份空白配置,把用户已有的收藏和最近使用抹掉。
|
||||||
|
// user_id 上有唯一索引,这种误写多半会撞唯一键而失败,但那是运气不是设计。
|
||||||
|
type UserXAppCenterRepo struct{ *QueryBuilder }
|
||||||
|
|
||||||
|
// FindByUser 取某用户的应用中心配置。三种结果:
|
||||||
|
// - row != nil 找到
|
||||||
|
// - row == nil, err == nil 该用户还没有配置,调用方应新建(不是错误)
|
||||||
|
// - err != nil 读取出错,调用方应报错,**不要**当成「没有配置」
|
||||||
|
func (r UserXAppCenterRepo) FindByUser(userID uint) (*xappmodel.UserXAppCenter, error) {
|
||||||
|
var row xappmodel.UserXAppCenter
|
||||||
|
err := r.Inner().Where("user_id = ?", userID).First(&row).Error
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &row, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save 保存应用中心配置(新建或更新,由 row.ID 是否为零值决定)。
|
||||||
|
func (r UserXAppCenterRepo) Save(row *xappmodel.UserXAppCenter) bool {
|
||||||
|
return r.QueryBuilder.Save(row)
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
xappmodel "eai_agentplatform/backend/internal/xapps/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
// XAppDefinition 应用定义仓库。
|
||||||
|
//
|
||||||
|
// 应用(App)是对象层三类一级对象之一,定位是「长程任务运行壳」。
|
||||||
|
// 本仓库只管应用目录本身的取数;用户自己的应用中心状态见 UserXAppCenterRepo。
|
||||||
|
type XAppDefinitionRepo struct{ *QueryBuilder }
|
||||||
|
|
||||||
|
// List 应用定义列表(sort_order ASC, id ASC)。
|
||||||
|
//
|
||||||
|
// state 与 exposedToUser 的约定同 SkillDefinitionRepo.List:
|
||||||
|
// state 的默认值由 handler 定,exposedToUser 用三态指针区分「不传」与「传 false」。
|
||||||
|
func (r XAppDefinitionRepo) List(state string, exposedToUser *bool) []xappmodel.XAppDefinition {
|
||||||
|
q := r.Type(&xappmodel.XAppDefinition{})
|
||||||
|
if state != "" {
|
||||||
|
q = q.Where("state = ?", state)
|
||||||
|
}
|
||||||
|
if exposedToUser != nil {
|
||||||
|
q = q.Where("exposed_to_user = ?", *exposedToUser)
|
||||||
|
}
|
||||||
|
var items []xappmodel.XAppDefinition
|
||||||
|
if q.Order("sort_order ASC, id ASC").Find(&items) {
|
||||||
|
return items
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetByID 按 ID 取。
|
||||||
|
func (r XAppDefinitionRepo) GetByID(id uint) (xappmodel.XAppDefinition, bool) {
|
||||||
|
var a xappmodel.XAppDefinition
|
||||||
|
if r.Type(&a).Where("id = ?", id).First(&a) {
|
||||||
|
return a, true
|
||||||
|
}
|
||||||
|
return xappmodel.XAppDefinition{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetByKey 按 key 取(key 上有唯一索引)。
|
||||||
|
func (r XAppDefinitionRepo) GetByKey(key string) (xappmodel.XAppDefinition, bool) {
|
||||||
|
var a xappmodel.XAppDefinition
|
||||||
|
if r.Type(&a).Where("key = ?", key).First(&a) {
|
||||||
|
return a, true
|
||||||
|
}
|
||||||
|
return xappmodel.XAppDefinition{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert 新建应用定义。
|
||||||
|
func (r XAppDefinitionRepo) Insert(a *xappmodel.XAppDefinition) bool {
|
||||||
|
return r.QueryBuilder.Insert(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update 更新应用定义。
|
||||||
|
func (r XAppDefinitionRepo) Update(a *xappmodel.XAppDefinition) bool {
|
||||||
|
return r.Save(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete 硬删除应用定义。
|
||||||
|
func (r XAppDefinitionRepo) Delete(a *xappmodel.XAppDefinition) bool {
|
||||||
|
return r.QueryBuilder.Delete(a)
|
||||||
|
}
|
||||||
@@ -5,11 +5,19 @@ import (
|
|||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
|
"eai_agentplatform/backend/internal/repository"
|
||||||
skillmodel "eai_agentplatform/backend/internal/skills/model"
|
skillmodel "eai_agentplatform/backend/internal/skills/model"
|
||||||
"eai_agentplatform/backend/internal/store"
|
|
||||||
"eai_agentplatform/backend/internal/web"
|
"eai_agentplatform/backend/internal/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// skillDefinitionRepo 技能定义仓库(便于测试时覆写)。query_handlers.go 里的读接口共用,
|
||||||
|
// 声明只此一处 —— 两份变量持有同一个仓库时,测试里覆写一份、另一份照旧,行为会静默分叉。
|
||||||
|
var skillDefinitionRepo repository.SkillDefinitionRepo
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
skillDefinitionRepo = repository.SkillDefinitionRepo{}
|
||||||
|
}
|
||||||
|
|
||||||
func CreateSkillDefinition(c *gin.Context) {
|
func CreateSkillDefinition(c *gin.Context) {
|
||||||
var req definitionReq
|
var req definitionReq
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
@@ -40,7 +48,7 @@ func CreateSkillDefinition(c *gin.Context) {
|
|||||||
State: req.State,
|
State: req.State,
|
||||||
SortOrder: req.SortOrder,
|
SortOrder: req.SortOrder,
|
||||||
}
|
}
|
||||||
if err := store.DB.Create(&item).Error; err != nil {
|
if !skillDefinitionRepo.Insert(&item) {
|
||||||
web.Fail(c, web.NewBadRequest("创建技能定义失败"))
|
web.Fail(c, web.NewBadRequest("创建技能定义失败"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -53,8 +61,8 @@ func UpdateSkillDefinition(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var item skillmodel.SkillDefinition
|
item, found := skillDefinitionRepo.GetByID(id)
|
||||||
if err := store.DB.First(&item, id).Error; err != nil {
|
if !found {
|
||||||
web.Fail(c, web.NewNotFoundError("技能定义不存在"))
|
web.Fail(c, web.NewNotFoundError("技能定义不存在"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -87,7 +95,7 @@ func UpdateSkillDefinition(c *gin.Context) {
|
|||||||
item.State = req.State
|
item.State = req.State
|
||||||
item.SortOrder = req.SortOrder
|
item.SortOrder = req.SortOrder
|
||||||
|
|
||||||
if err := store.DB.Save(&item).Error; err != nil {
|
if !skillDefinitionRepo.Update(&item) {
|
||||||
web.Fail(c, web.NewBadRequest("更新技能定义失败"))
|
web.Fail(c, web.NewBadRequest("更新技能定义失败"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -100,12 +108,12 @@ func DeleteSkillDefinition(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var item skillmodel.SkillDefinition
|
item, found := skillDefinitionRepo.GetByID(id)
|
||||||
if err := store.DB.First(&item, id).Error; err != nil {
|
if !found {
|
||||||
web.Fail(c, web.NewNotFoundError("技能定义不存在"))
|
web.Fail(c, web.NewNotFoundError("技能定义不存在"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := store.DB.Delete(&item).Error; err != nil {
|
if !skillDefinitionRepo.Delete(&item) {
|
||||||
web.Fail(c, web.NewBadRequest("删除技能定义失败"))
|
web.Fail(c, web.NewBadRequest("删除技能定义失败"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,28 +5,23 @@ import (
|
|||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
skillmodel "eai_agentplatform/backend/internal/skills/model"
|
|
||||||
"eai_agentplatform/backend/internal/store"
|
|
||||||
"eai_agentplatform/backend/internal/web"
|
"eai_agentplatform/backend/internal/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ListSkillDefinitions(c *gin.Context) {
|
func ListSkillDefinitions(c *gin.Context) {
|
||||||
q := store.DB.Model(&skillmodel.SkillDefinition{})
|
// 不传 state 默认只看 active —— 这是列表接口的契约,放在这里解析。
|
||||||
if c.Query("state") == "" {
|
state := c.Query("state")
|
||||||
q = q.Where("state = ?", "active")
|
if state == "" {
|
||||||
} else {
|
state = "active"
|
||||||
q = q.Where("state = ?", c.Query("state"))
|
|
||||||
}
|
}
|
||||||
if c.Query("exposed_to_user") != "" {
|
// exposed_to_user 三态:不传 = 不过滤,传了按 true/false 精确匹配
|
||||||
q = q.Where("exposed_to_user = ?", c.Query("exposed_to_user") == "true")
|
// (原实现是 `!= "true"` 一律当 false,这里保持同一口径)。
|
||||||
|
var exposed *bool
|
||||||
|
if v := c.Query("exposed_to_user"); v != "" {
|
||||||
|
b := v == "true"
|
||||||
|
exposed = &b
|
||||||
}
|
}
|
||||||
|
web.OK(c, skillDefinitionRepo.List(state, exposed))
|
||||||
var items []skillmodel.SkillDefinition
|
|
||||||
if err := q.Order("sort_order ASC, id ASC").Find(&items).Error; err != nil {
|
|
||||||
web.Fail(c, web.NewBadRequest("查询技能定义失败"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
web.OK(c, items)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetSkillDefinitionByKey(c *gin.Context) {
|
func GetSkillDefinitionByKey(c *gin.Context) {
|
||||||
@@ -36,8 +31,8 @@ func GetSkillDefinitionByKey(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var item skillmodel.SkillDefinition
|
item, found := skillDefinitionRepo.GetByKey(key)
|
||||||
if err := store.DB.Where("key = ?", key).First(&item).Error; err != nil {
|
if !found {
|
||||||
web.Fail(c, web.NewNotFoundError("技能定义不存在"))
|
web.Fail(c, web.NewNotFoundError("技能定义不存在"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,22 +2,31 @@ package xappapi
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"gorm.io/gorm"
|
|
||||||
|
|
||||||
"eai_agentplatform/backend/internal/jsonutil"
|
"eai_agentplatform/backend/internal/jsonutil"
|
||||||
"eai_agentplatform/backend/internal/middleware"
|
"eai_agentplatform/backend/internal/middleware"
|
||||||
"eai_agentplatform/backend/internal/store"
|
"eai_agentplatform/backend/internal/repository"
|
||||||
"eai_agentplatform/backend/internal/web"
|
"eai_agentplatform/backend/internal/web"
|
||||||
xappdefs "eai_agentplatform/backend/internal/xapps/model"
|
xappdefs "eai_agentplatform/backend/internal/xapps/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// 应用目录与用户应用中心两个仓库(便于测试时覆写),本包共用。
|
||||||
|
var (
|
||||||
|
xAppDefinitionRepo repository.XAppDefinitionRepo
|
||||||
|
userXAppCenterRepo repository.UserXAppCenterRepo
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
xAppDefinitionRepo = repository.XAppDefinitionRepo{}
|
||||||
|
userXAppCenterRepo = repository.UserXAppCenterRepo{}
|
||||||
|
}
|
||||||
|
|
||||||
type definitionReq struct {
|
type definitionReq struct {
|
||||||
Key string `json:"key"`
|
Key string `json:"key"`
|
||||||
Label string `json:"label"`
|
Label string `json:"label"`
|
||||||
@@ -114,21 +123,19 @@ func validateDefinitionReq(req *definitionReq) *web.AppError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ListXAppDefinitions(c *gin.Context) {
|
func ListXAppDefinitions(c *gin.Context) {
|
||||||
q := store.DB.Model(&xappdefs.XAppDefinition{})
|
// 不传 state 默认只看 active —— 这是列表接口的契约,放在这里解析。
|
||||||
if c.Query("state") == "" {
|
state := c.Query("state")
|
||||||
q = q.Where("state = ?", "active")
|
if state == "" {
|
||||||
} else {
|
state = "active"
|
||||||
q = q.Where("state = ?", c.Query("state"))
|
|
||||||
}
|
}
|
||||||
if c.Query("exposed_to_user") != "" {
|
// exposed_to_user 三态:不传 = 不过滤,传了按 true/false 精确匹配
|
||||||
q = q.Where("exposed_to_user = ?", c.Query("exposed_to_user") == "true")
|
// (原实现是 `!= "true"` 一律当 false,这里保持同一口径)。
|
||||||
|
var exposed *bool
|
||||||
|
if v := c.Query("exposed_to_user"); v != "" {
|
||||||
|
b := v == "true"
|
||||||
|
exposed = &b
|
||||||
}
|
}
|
||||||
var items []xappdefs.XAppDefinition
|
web.OK(c, xAppDefinitionRepo.List(state, exposed))
|
||||||
if err := q.Order("sort_order ASC, id ASC").Find(&items).Error; err != nil {
|
|
||||||
web.Fail(c, web.NewBadRequest("查询应用定义失败"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
web.OK(c, items)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetXAppDefinitionByKey(c *gin.Context) {
|
func GetXAppDefinitionByKey(c *gin.Context) {
|
||||||
@@ -137,8 +144,8 @@ func GetXAppDefinitionByKey(c *gin.Context) {
|
|||||||
web.Fail(c, web.NewBadRequest("应用 key 不能为空"))
|
web.Fail(c, web.NewBadRequest("应用 key 不能为空"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var item xappdefs.XAppDefinition
|
item, found := xAppDefinitionRepo.GetByKey(key)
|
||||||
if err := store.DB.Where("key = ?", key).First(&item).Error; err != nil {
|
if !found {
|
||||||
web.Fail(c, web.NewNotFoundError("应用定义不存在"))
|
web.Fail(c, web.NewNotFoundError("应用定义不存在"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -180,7 +187,7 @@ func CreateXAppDefinition(c *gin.Context) {
|
|||||||
State: req.State,
|
State: req.State,
|
||||||
SortOrder: req.SortOrder,
|
SortOrder: req.SortOrder,
|
||||||
}
|
}
|
||||||
if err := store.DB.Create(&item).Error; err != nil {
|
if !xAppDefinitionRepo.Insert(&item) {
|
||||||
web.Fail(c, web.NewBadRequest("创建应用定义失败"))
|
web.Fail(c, web.NewBadRequest("创建应用定义失败"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -192,8 +199,8 @@ func UpdateXAppDefinition(c *gin.Context) {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var item xappdefs.XAppDefinition
|
item, found := xAppDefinitionRepo.GetByID(id)
|
||||||
if err := store.DB.First(&item, id).Error; err != nil {
|
if !found {
|
||||||
web.Fail(c, web.NewNotFoundError("应用定义不存在"))
|
web.Fail(c, web.NewNotFoundError("应用定义不存在"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -229,7 +236,7 @@ func UpdateXAppDefinition(c *gin.Context) {
|
|||||||
item.ExposedToUser = req.ExposedToUser
|
item.ExposedToUser = req.ExposedToUser
|
||||||
item.State = req.State
|
item.State = req.State
|
||||||
item.SortOrder = req.SortOrder
|
item.SortOrder = req.SortOrder
|
||||||
if err := store.DB.Save(&item).Error; err != nil {
|
if !xAppDefinitionRepo.Update(&item) {
|
||||||
web.Fail(c, web.NewBadRequest("更新应用定义失败"))
|
web.Fail(c, web.NewBadRequest("更新应用定义失败"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -241,12 +248,12 @@ func DeleteXAppDefinition(c *gin.Context) {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var item xappdefs.XAppDefinition
|
item, found := xAppDefinitionRepo.GetByID(id)
|
||||||
if err := store.DB.First(&item, id).Error; err != nil {
|
if !found {
|
||||||
web.Fail(c, web.NewNotFoundError("应用定义不存在"))
|
web.Fail(c, web.NewNotFoundError("应用定义不存在"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := store.DB.Delete(&item).Error; err != nil {
|
if !xAppDefinitionRepo.Delete(&item) {
|
||||||
web.Fail(c, web.NewBadRequest("删除应用定义失败"))
|
web.Fail(c, web.NewBadRequest("删除应用定义失败"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -290,7 +297,7 @@ func GetMyXAppCenter(c *gin.Context) {
|
|||||||
web.Fail(c, web.NewAuthError("未登录"))
|
web.Fail(c, web.NewAuthError("未登录"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
row, err := findUserXAppCenter(user.ID)
|
row, err := userXAppCenterRepo.FindByUser(user.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
web.Fail(c, web.NewBadRequest("读取应用中心失败"))
|
web.Fail(c, web.NewBadRequest("读取应用中心失败"))
|
||||||
return
|
return
|
||||||
@@ -314,7 +321,7 @@ func UpdateMyXAppCenter(c *gin.Context) {
|
|||||||
RecentKeys: mustJSONXAppCenter(req.RecentKeys),
|
RecentKeys: mustJSONXAppCenter(req.RecentKeys),
|
||||||
CustomXApps: mustJSONXAppCenter(req.CustomXApps),
|
CustomXApps: mustJSONXAppCenter(req.CustomXApps),
|
||||||
})
|
})
|
||||||
row, err := findUserXAppCenter(user.ID)
|
row, err := userXAppCenterRepo.FindByUser(user.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
web.Fail(c, web.NewBadRequest("读取应用中心失败"))
|
web.Fail(c, web.NewBadRequest("读取应用中心失败"))
|
||||||
return
|
return
|
||||||
@@ -325,25 +332,13 @@ func UpdateMyXAppCenter(c *gin.Context) {
|
|||||||
row.FavoriteKeys = mustJSONXAppCenter(payload.FavoriteKeys)
|
row.FavoriteKeys = mustJSONXAppCenter(payload.FavoriteKeys)
|
||||||
row.RecentKeys = mustJSONXAppCenter(payload.RecentKeys)
|
row.RecentKeys = mustJSONXAppCenter(payload.RecentKeys)
|
||||||
row.CustomXApps = mustJSONXAppCenter(payload.CustomXApps)
|
row.CustomXApps = mustJSONXAppCenter(payload.CustomXApps)
|
||||||
if err := store.DB.Save(row).Error; err != nil {
|
if !userXAppCenterRepo.Save(row) {
|
||||||
web.Fail(c, web.NewBadRequest("保存应用中心失败"))
|
web.Fail(c, web.NewBadRequest("保存应用中心失败"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
web.OK(c, payload)
|
web.OK(c, payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
func findUserXAppCenter(userID uint) (*xappdefs.UserXAppCenter, error) {
|
|
||||||
var row xappdefs.UserXAppCenter
|
|
||||||
err := store.DB.Where("user_id = ?", userID).First(&row).Error
|
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &row, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func normalizeXAppCenterPayload(row *xappdefs.UserXAppCenter) centerPayload {
|
func normalizeXAppCenterPayload(row *xappdefs.UserXAppCenter) centerPayload {
|
||||||
if row == nil {
|
if row == nil {
|
||||||
return centerPayload{
|
return centerPayload{
|
||||||
|
|||||||
Reference in New Issue
Block a user