feat: 收敛店铺管理和蝦皮分类搜索 (#212 #213)

This commit is contained in:
chengma
2026-08-13 11:47:56 +08:00
parent 1abed648a5
commit 313e9f9151
22 changed files with 389 additions and 464 deletions
+32 -53
View File
@@ -33,15 +33,11 @@ func ListShops(db *sql.DB, actor *model.User) (ShopListResult, error) {
return ShopListResult{Items: items, UnlinkedShopeeCount: unlinked}, nil
}
func ShopOptions(db *sql.DB) ([]model.Shop, error) {
return repository.ListShopOptions(db)
}
func CreateShop(db *sql.DB, actor *model.User, displayName, sybAlias, shopeeAlias string, now time.Time) error {
func CreateShop(db *sql.DB, actor *model.User, shopName string, now time.Time) error {
if actor == nil || !actor.IsAdmin() {
return ErrAdminRequired
}
displayName, sybAlias, shopeeAlias, err := validateShopInput(displayName, sybAlias, shopeeAlias)
shopName, err := validateShopName(shopName)
if err != nil {
return err
}
@@ -55,14 +51,14 @@ func CreateShop(db *sql.DB, actor *model.User, displayName, sybAlias, shopeeAlia
return err
}
defer tx.Rollback()
if err := repository.InsertShop(tx, model.Shop{ShopID: shopID, DisplayName: displayName,
NormalizedName: displayName, Enabled: true, CreatedByUserID: actor.UserID, CreatedAt: at, UpdatedAt: at}); err != nil {
if err := repository.InsertShop(tx, model.Shop{ShopID: shopID, DisplayName: shopName,
NormalizedName: shopName, Enabled: true, CreatedByUserID: actor.UserID, CreatedAt: at, UpdatedAt: at}); err != nil {
return shopValidationError(err)
}
if err := insertShopAlias(tx, shopID, "syb", sybAlias, true, at); err != nil {
if err := insertShopAlias(tx, shopID, "syb", shopName, true, at); err != nil {
return shopValidationError(err)
}
if err := insertShopAlias(tx, shopID, "shopee", shopeeAlias, true, at); err != nil {
if err := insertShopAlias(tx, shopID, "shopee", shopName, true, at); err != nil {
return shopValidationError(err)
}
if err := repository.RebuildShopAssociations(tx, shopID); err != nil {
@@ -71,7 +67,7 @@ func CreateShop(db *sql.DB, actor *model.User, displayName, sybAlias, shopeeAlia
return tx.Commit()
}
func UpdateShop(db *sql.DB, actor *model.User, shopID, displayName, sybAlias, shopeeAlias string, now time.Time) error {
func UpdateShop(db *sql.DB, actor *model.User, shopID, shopName string, now time.Time) error {
if actor == nil || !actor.IsAdmin() {
return ErrAdminRequired
}
@@ -79,7 +75,7 @@ func UpdateShop(db *sql.DB, actor *model.User, shopID, displayName, sybAlias, sh
if shopID == "" {
return &validationError{field: "shop_id", message: "店铺编号不能为空"}
}
displayName, sybAlias, shopeeAlias, err := validateShopInput(displayName, sybAlias, shopeeAlias)
shopName, err := validateShopName(shopName)
if err != nil {
return err
}
@@ -89,25 +85,19 @@ func UpdateShop(db *sql.DB, actor *model.User, shopID, displayName, sybAlias, sh
return err
}
defer tx.Rollback()
if _, found, err := repository.GetShop(tx, shopID); err != nil {
shop, found, err := repository.GetShop(tx, shopID)
if err != nil {
return err
} else if !found {
return &validationError{field: "shop_id", message: "店铺不存在,请刷新页面后重试"}
}
sybEnabled, hasSybAlias, err := repository.GetShopAliasEnabled(tx, shopID, "syb")
if err != nil {
return err
}
if !hasSybAlias {
sybEnabled = true
}
if _, err := repository.UpdateShopName(tx, shopID, displayName, displayName, at); err != nil {
if _, err := repository.UpdateShopName(tx, shopID, shopName, shopName, at); err != nil {
return shopValidationError(err)
}
if err := replaceShopAlias(tx, shopID, "syb", sybAlias, sybEnabled, at); err != nil {
if err := replaceShopAlias(tx, shopID, "syb", shopName, shop.Enabled, at); err != nil {
return shopValidationError(err)
}
if err := replaceShopAlias(tx, shopID, "shopee", shopeeAlias, true, at); err != nil {
if err := replaceShopAlias(tx, shopID, "shopee", shopName, shop.Enabled, at); err != nil {
return shopValidationError(err)
}
if err := repository.RebuildShopAssociations(tx, shopID); err != nil {
@@ -120,28 +110,24 @@ func SetShopEnabled(db *sql.DB, actor *model.User, shopID string, enabled bool,
if actor == nil || !actor.IsAdmin() {
return ErrAdminRequired
}
found, err := repository.SetShopEnabled(db, strings.TrimSpace(shopID), enabled, now.UTC().Format(model.TimeLayout))
shopID = strings.TrimSpace(shopID)
at := now.UTC().Format(model.TimeLayout)
tx, err := db.Begin()
if err != nil {
return err
}
defer tx.Rollback()
found, err := repository.SetShopEnabled(tx, shopID, enabled, at)
if err != nil {
return err
}
if !found {
return &validationError{field: "shop_id", message: "店铺不存在,请刷新页面后重试"}
}
return nil
}
func SetShopSybEnabled(db *sql.DB, actor *model.User, shopID string, enabled bool, now time.Time) error {
if actor == nil || !actor.IsAdmin() {
return ErrAdminRequired
}
found, err := repository.SetShopSybEnabled(db, strings.TrimSpace(shopID), enabled, now.UTC().Format(model.TimeLayout))
if err != nil {
if err := repository.SetShopCompatibilityAliasesEnabled(tx, shopID, enabled, at); err != nil {
return err
}
if !found {
return &validationError{field: "shop_id", message: "该店铺没有配置 SYB 店铺名称"}
}
return nil
return tx.Commit()
}
func DeleteShop(db *sql.DB, actor *model.User, shopID string) error {
@@ -161,22 +147,15 @@ func DeleteShop(db *sql.DB, actor *model.User, shopID string) error {
return nil
}
func validateShopInput(displayName, sybAlias, shopeeAlias string) (string, string, string, error) {
displayName = strings.TrimSpace(displayName)
sybAlias = strings.TrimSpace(sybAlias)
shopeeAlias = strings.TrimSpace(shopeeAlias)
if displayName == "" {
return "", "", "", &validationError{field: "display_name", message: "业务店铺名称不能为空"}
func validateShopName(shopName string) (string, error) {
shopName = strings.TrimSpace(shopName)
if shopName == "" {
return "", &validationError{field: "shop_name", message: "店铺名称不能为空"}
}
for field, value := range map[string]string{"display_name": displayName, "syb_alias": sybAlias, "shopee_alias": shopeeAlias} {
if len([]rune(value)) > maxShopNameLength {
return "", "", "", &validationError{field: field, message: "店铺名称最多 500 个字符"}
}
if len([]rune(shopName)) > maxShopNameLength {
return "", &validationError{field: "shop_name", message: "店铺名称最多 500 个字符"}
}
if sybAlias == "" && shopeeAlias == "" {
return "", "", "", &validationError{field: "syb_alias", message: "SYB 或蝦皮店铺名称至少填写一个"}
}
return displayName, sybAlias, shopeeAlias, nil
return shopName, nil
}
func insertShopAlias(q repository.Execer, shopID, channel, alias string, enabled bool, at string) error {
@@ -202,9 +181,9 @@ func replaceShopAlias(q repository.Execer, shopID, channel, alias string, enable
func shopValidationError(err error) error {
switch {
case errors.Is(err, repository.ErrShopNameExists):
return &validationError{field: "display_name", message: "业务店铺名称已经存在"}
return &validationError{field: "shop_name", message: "店铺名称已经存在"}
case errors.Is(err, repository.ErrShopAliasExists):
return &validationError{field: "syb_alias", message: "该渠道店铺名称已关联到其他业务店铺"}
return &validationError{field: "shop_name", message: "店铺名称已经存在"}
default:
return err
}