Files
junhong_cmp_fiber/tests/integration/platform_account_test.go
huang 91c9bbfeb8
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 4m35s
feat: 实现账号与佣金管理模块
新增功能:
- 店铺佣金查询:店铺佣金统计、店铺佣金记录列表、店铺提现记录
- 佣金提现审批:提现申请列表、审批通过、审批拒绝
- 提现配置管理:配置列表、新增配置、获取当前生效配置
- 企业管理:企业列表、创建、更新、删除、获取详情
- 企业卡授权:授权列表、批量授权、批量取消授权、统计
- 客户账号管理:账号列表、创建、更新状态、重置密码
- 我的佣金:佣金统计、佣金记录、提现申请、提现记录

数据库变更:
- 扩展 tb_commission_withdrawal_request 新增提现单号等字段
- 扩展 tb_account 新增 is_primary 字段
- 扩展 tb_commission_record 新增 shop_id、balance_after
- 扩展 tb_commission_withdrawal_setting 新增每日提现次数限制
- 扩展 tb_iot_card、tb_device 新增 shop_id 冗余字段
- 新建 tb_enterprise_card_authorization 企业卡授权表
- 新建 tb_asset_allocation_record 资产分配记录表
- 数据迁移:owner_type 枚举值 agent 统一为 shop

测试:
- 新增 7 个单元测试文件覆盖各服务
- 修复集成测试 Redis 依赖问题
2026-01-21 18:20:44 +08:00

360 lines
12 KiB
Go

package integration
import (
"bytes"
"encoding/json"
"fmt"
"net/http/httptest"
"testing"
"github.com/gofiber/fiber/v2"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/break/junhong_cmp_fiber/internal/bootstrap"
"github.com/break/junhong_cmp_fiber/internal/handler/admin"
"github.com/break/junhong_cmp_fiber/internal/model"
"github.com/break/junhong_cmp_fiber/internal/routes"
accountService "github.com/break/junhong_cmp_fiber/internal/service/account"
postgresStore "github.com/break/junhong_cmp_fiber/internal/store/postgres"
"github.com/break/junhong_cmp_fiber/pkg/constants"
"github.com/break/junhong_cmp_fiber/pkg/errors"
"github.com/break/junhong_cmp_fiber/pkg/middleware"
"github.com/break/junhong_cmp_fiber/pkg/response"
"github.com/break/junhong_cmp_fiber/tests/testutils"
)
func TestPlatformAccountAPI_ListPlatformAccounts(t *testing.T) {
db, redisClient := testutils.SetupTestDB(t)
defer testutils.TeardownTestDB(t, db, redisClient)
accountStore := postgresStore.NewAccountStore(db, redisClient)
roleStore := postgresStore.NewRoleStore(db)
accountRoleStore := postgresStore.NewAccountRoleStore(db, redisClient)
accService := accountService.New(accountStore, roleStore, accountRoleStore)
accountHandler := admin.NewAccountHandler(accService)
app := fiber.New(fiber.Config{
ErrorHandler: errors.SafeErrorHandler(nil),
})
testUserID := uint(1)
app.Use(func(c *fiber.Ctx) error {
ctx := middleware.SetUserContext(c.UserContext(), middleware.NewSimpleUserContext(testUserID, constants.UserTypeSuperAdmin, 0))
c.SetUserContext(ctx)
return c.Next()
})
services := &bootstrap.Handlers{Account: accountHandler}
middlewares := &bootstrap.Middlewares{}
routes.RegisterRoutes(app, services, middlewares)
superAdmin := &model.Account{
Username: testutils.GenerateUsername("super_admin", 1),
Phone: testutils.GeneratePhone("138", 1),
Password: "hashedpassword",
UserType: constants.UserTypeSuperAdmin,
Status: constants.StatusEnabled,
}
db.Create(superAdmin)
platformUser := &model.Account{
Username: testutils.GenerateUsername("platform_user", 2),
Phone: testutils.GeneratePhone("138", 2),
Password: "hashedpassword",
UserType: constants.UserTypePlatform,
Status: constants.StatusEnabled,
}
db.Create(platformUser)
agentUser := &model.Account{
Username: testutils.GenerateUsername("agent_user", 3),
Phone: testutils.GeneratePhone("138", 3),
Password: "hashedpassword",
UserType: constants.UserTypeAgent,
Status: constants.StatusEnabled,
}
db.Create(agentUser)
t.Run("列表只返回平台账号和超级管理员", func(t *testing.T) {
req := httptest.NewRequest("GET", "/api/admin/platform-accounts?page=1&page_size=20", nil)
resp, err := app.Test(req)
require.NoError(t, err)
assert.Equal(t, fiber.StatusOK, resp.StatusCode)
var result response.Response
err = json.NewDecoder(resp.Body).Decode(&result)
require.NoError(t, err)
assert.Equal(t, 0, result.Code)
data := result.Data.(map[string]interface{})
items := data["items"].([]interface{})
assert.GreaterOrEqual(t, len(items), 2)
var count int64
db.Model(&model.Account{}).Where("user_type IN ?", []int{1, 2}).Count(&count)
assert.GreaterOrEqual(t, count, int64(2))
})
t.Run("按用户名筛选", func(t *testing.T) {
req := httptest.NewRequest("GET", "/api/admin/platform-accounts?username=platform_user", nil)
resp, err := app.Test(req)
require.NoError(t, err)
assert.Equal(t, fiber.StatusOK, resp.StatusCode)
var result response.Response
err = json.NewDecoder(resp.Body).Decode(&result)
require.NoError(t, err)
data := result.Data.(map[string]interface{})
items := data["items"].([]interface{})
assert.GreaterOrEqual(t, len(items), 1)
})
}
func TestPlatformAccountAPI_UpdatePassword(t *testing.T) {
db, redisClient := testutils.SetupTestDB(t)
defer testutils.TeardownTestDB(t, db, redisClient)
accountStore := postgresStore.NewAccountStore(db, redisClient)
roleStore := postgresStore.NewRoleStore(db)
accountRoleStore := postgresStore.NewAccountRoleStore(db, redisClient)
accService := accountService.New(accountStore, roleStore, accountRoleStore)
accountHandler := admin.NewAccountHandler(accService)
app := fiber.New(fiber.Config{
ErrorHandler: errors.SafeErrorHandler(nil),
})
testUserID := uint(1)
app.Use(func(c *fiber.Ctx) error {
ctx := middleware.SetUserContext(c.UserContext(), middleware.NewSimpleUserContext(testUserID, constants.UserTypeSuperAdmin, 0))
c.SetUserContext(ctx)
return c.Next()
})
services := &bootstrap.Handlers{Account: accountHandler}
middlewares := &bootstrap.Middlewares{}
routes.RegisterRoutes(app, services, middlewares)
testAccount := &model.Account{
Username: testutils.GenerateUsername("pwd_test", 10),
Phone: testutils.GeneratePhone("139", 10),
Password: "old_hashed_password",
UserType: constants.UserTypePlatform,
Status: constants.StatusEnabled,
}
db.Create(testAccount)
t.Run("成功修改密码", func(t *testing.T) {
reqBody := model.UpdatePasswordRequest{
NewPassword: "NewPassword@123",
}
jsonBody, _ := json.Marshal(reqBody)
req := httptest.NewRequest("PUT", fmt.Sprintf("/api/admin/platform-accounts/%d/password", testAccount.ID), bytes.NewReader(jsonBody))
req.Header.Set("Content-Type", "application/json")
resp, err := app.Test(req)
require.NoError(t, err)
assert.Equal(t, fiber.StatusOK, resp.StatusCode)
var result response.Response
err = json.NewDecoder(resp.Body).Decode(&result)
require.NoError(t, err)
assert.Equal(t, 0, result.Code)
var updated model.Account
db.First(&updated, testAccount.ID)
assert.NotEqual(t, "old_hashed_password", updated.Password)
})
t.Run("账号不存在返回错误", func(t *testing.T) {
reqBody := model.UpdatePasswordRequest{
NewPassword: "NewPassword@123",
}
jsonBody, _ := json.Marshal(reqBody)
req := httptest.NewRequest("PUT", "/api/admin/platform-accounts/99999/password", bytes.NewReader(jsonBody))
req.Header.Set("Content-Type", "application/json")
resp, err := app.Test(req)
require.NoError(t, err)
var result response.Response
err = json.NewDecoder(resp.Body).Decode(&result)
require.NoError(t, err)
assert.Equal(t, errors.CodeAccountNotFound, result.Code)
})
}
func TestPlatformAccountAPI_UpdateStatus(t *testing.T) {
db, redisClient := testutils.SetupTestDB(t)
defer testutils.TeardownTestDB(t, db, redisClient)
accountStore := postgresStore.NewAccountStore(db, redisClient)
roleStore := postgresStore.NewRoleStore(db)
accountRoleStore := postgresStore.NewAccountRoleStore(db, redisClient)
accService := accountService.New(accountStore, roleStore, accountRoleStore)
accountHandler := admin.NewAccountHandler(accService)
app := fiber.New(fiber.Config{
ErrorHandler: errors.SafeErrorHandler(nil),
})
testUserID := uint(1)
app.Use(func(c *fiber.Ctx) error {
ctx := middleware.SetUserContext(c.UserContext(), middleware.NewSimpleUserContext(testUserID, constants.UserTypeSuperAdmin, 0))
c.SetUserContext(ctx)
return c.Next()
})
services := &bootstrap.Handlers{Account: accountHandler}
middlewares := &bootstrap.Middlewares{}
routes.RegisterRoutes(app, services, middlewares)
testAccount := &model.Account{
Username: testutils.GenerateUsername("status_test", 20),
Phone: testutils.GeneratePhone("137", 20),
Password: "hashedpassword",
UserType: constants.UserTypePlatform,
Status: constants.StatusEnabled,
}
db.Create(testAccount)
t.Run("成功禁用账号", func(t *testing.T) {
reqBody := model.UpdateStatusRequest{
Status: constants.StatusDisabled,
}
jsonBody, _ := json.Marshal(reqBody)
req := httptest.NewRequest("PUT", fmt.Sprintf("/api/admin/platform-accounts/%d/status", testAccount.ID), bytes.NewReader(jsonBody))
req.Header.Set("Content-Type", "application/json")
resp, err := app.Test(req)
require.NoError(t, err)
assert.Equal(t, fiber.StatusOK, resp.StatusCode)
var updated model.Account
db.First(&updated, testAccount.ID)
assert.Equal(t, constants.StatusDisabled, updated.Status)
})
t.Run("成功启用账号", func(t *testing.T) {
reqBody := model.UpdateStatusRequest{
Status: constants.StatusEnabled,
}
jsonBody, _ := json.Marshal(reqBody)
req := httptest.NewRequest("PUT", fmt.Sprintf("/api/admin/platform-accounts/%d/status", testAccount.ID), bytes.NewReader(jsonBody))
req.Header.Set("Content-Type", "application/json")
resp, err := app.Test(req)
require.NoError(t, err)
assert.Equal(t, fiber.StatusOK, resp.StatusCode)
var updated model.Account
db.First(&updated, testAccount.ID)
assert.Equal(t, constants.StatusEnabled, updated.Status)
})
}
func TestPlatformAccountAPI_AssignRoles(t *testing.T) {
db, redisClient := testutils.SetupTestDB(t)
defer testutils.TeardownTestDB(t, db, redisClient)
accountStore := postgresStore.NewAccountStore(db, redisClient)
roleStore := postgresStore.NewRoleStore(db)
accountRoleStore := postgresStore.NewAccountRoleStore(db, redisClient)
accService := accountService.New(accountStore, roleStore, accountRoleStore)
accountHandler := admin.NewAccountHandler(accService)
app := fiber.New(fiber.Config{
ErrorHandler: errors.SafeErrorHandler(nil),
})
testUserID := uint(1)
app.Use(func(c *fiber.Ctx) error {
ctx := middleware.SetUserContext(c.UserContext(), middleware.NewSimpleUserContext(testUserID, constants.UserTypeSuperAdmin, 0))
c.SetUserContext(ctx)
return c.Next()
})
services := &bootstrap.Handlers{Account: accountHandler}
middlewares := &bootstrap.Middlewares{}
routes.RegisterRoutes(app, services, middlewares)
superAdmin := &model.Account{
Username: testutils.GenerateUsername("super_admin_role", 30),
Phone: testutils.GeneratePhone("136", 30),
Password: "hashedpassword",
UserType: constants.UserTypeSuperAdmin,
Status: constants.StatusEnabled,
}
db.Create(superAdmin)
platformUser := &model.Account{
Username: testutils.GenerateUsername("platform_user_role", 31),
Phone: testutils.GeneratePhone("136", 31),
Password: "hashedpassword",
UserType: constants.UserTypePlatform,
Status: constants.StatusEnabled,
}
db.Create(platformUser)
testRole := &model.Role{
RoleName: testutils.GenerateUsername("测试角色", 30),
RoleType: constants.RoleTypePlatform,
Status: constants.StatusEnabled,
}
db.Create(testRole)
t.Run("超级管理员禁止分配角色", func(t *testing.T) {
reqBody := model.AssignRolesRequest{
RoleIDs: []uint{testRole.ID},
}
jsonBody, _ := json.Marshal(reqBody)
req := httptest.NewRequest("POST", fmt.Sprintf("/api/admin/platform-accounts/%d/roles", superAdmin.ID), bytes.NewReader(jsonBody))
req.Header.Set("Content-Type", "application/json")
resp, err := app.Test(req)
require.NoError(t, err)
var result response.Response
err = json.NewDecoder(resp.Body).Decode(&result)
require.NoError(t, err)
assert.Equal(t, errors.CodeInvalidParam, result.Code)
assert.Contains(t, result.Message, "超级管理员不允许分配角色")
})
t.Run("平台用户成功分配角色", func(t *testing.T) {
reqBody := model.AssignRolesRequest{
RoleIDs: []uint{testRole.ID},
}
jsonBody, _ := json.Marshal(reqBody)
req := httptest.NewRequest("POST", fmt.Sprintf("/api/admin/platform-accounts/%d/roles", platformUser.ID), bytes.NewReader(jsonBody))
req.Header.Set("Content-Type", "application/json")
resp, err := app.Test(req)
require.NoError(t, err)
assert.Equal(t, fiber.StatusOK, resp.StatusCode)
var count int64
db.Model(&model.AccountRole{}).Where("account_id = ? AND role_id = ?", platformUser.ID, testRole.ID).Count(&count)
assert.Equal(t, int64(1), count)
})
t.Run("空数组清空所有角色", func(t *testing.T) {
reqBody := model.AssignRolesRequest{
RoleIDs: []uint{},
}
jsonBody, _ := json.Marshal(reqBody)
req := httptest.NewRequest("POST", fmt.Sprintf("/api/admin/platform-accounts/%d/roles", platformUser.ID), bytes.NewReader(jsonBody))
req.Header.Set("Content-Type", "application/json")
resp, err := app.Test(req)
require.NoError(t, err)
assert.Equal(t, fiber.StatusOK, resp.StatusCode)
var count int64
db.Model(&model.AccountRole{}).Where("account_id = ?", platformUser.ID).Count(&count)
assert.Equal(t, int64(0), count)
})
}