实现个人客户微信认证和短信验证功能

- 添加个人客户微信登录和手机验证码登录接口
- 实现个人客户设备、ICCID、手机号关联管理
- 添加短信发送服务(HTTP 客户端)
- 添加微信认证服务(含 mock 实现)
- 添加 JWT Token 生成和验证工具
- 创建数据库迁移脚本(personal_customer 关联表)
- 修复测试文件中的路由注册参数错误
- 重构 scripts 目录结构(分离独立脚本到子目录)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-10 11:42:38 +08:00
parent 1b9080e3ab
commit 9c6d4a3bd4
53 changed files with 4258 additions and 97 deletions

View File

@@ -120,7 +120,8 @@ func setupTestEnv(t *testing.T) *testEnv {
services := &bootstrap.Handlers{
Account: accountHandler,
}
routes.RegisterRoutes(app, services)
middlewares := &bootstrap.Middlewares{}
routes.RegisterRoutes(app, services, middlewares)
return &testEnv{
db: db,

View File

@@ -132,7 +132,8 @@ func setupRegressionTestEnv(t *testing.T) *regressionTestEnv {
Role: roleHandler,
Permission: permHandler,
}
routes.RegisterRoutes(app, services)
middlewares := &bootstrap.Middlewares{}
routes.RegisterRoutes(app, services, middlewares)
return &regressionTestEnv{
db: db,

View File

@@ -94,7 +94,8 @@ func setupPermTestEnv(t *testing.T) *permTestEnv {
services := &bootstrap.Handlers{
Permission: permHandler,
}
routes.RegisterRoutes(app, services)
middlewares := &bootstrap.Middlewares{}
routes.RegisterRoutes(app, services, middlewares)
return &permTestEnv{
db: db,

View File

@@ -120,7 +120,8 @@ func setupRoleTestEnv(t *testing.T) *roleTestEnv {
services := &bootstrap.Handlers{
Role: roleHandler,
}
routes.RegisterRoutes(app, services)
middlewares := &bootstrap.Middlewares{}
routes.RegisterRoutes(app, services, middlewares)
return &roleTestEnv{
db: db,

View File

@@ -29,20 +29,20 @@ func TestPersonalCustomerStore_Create(t *testing.T) {
{
name: "创建基本个人客户",
customer: &model.PersonalCustomer{
Phone: "13800000001",
Nickname: "测试用户A",
Status: constants.StatusEnabled,
WxOpenID: "wx_openid_test_a",
WxUnionID: "wx_unionid_test_a",
Nickname: "测试用户A",
Status: constants.StatusEnabled,
},
wantErr: false,
},
{
name: "创建带微信信息的个人客户",
customer: &model.PersonalCustomer{
Phone: "13800000002",
Nickname: "测试用户B",
AvatarURL: "https://example.com/avatar.jpg",
WxOpenID: "wx_openid_123456",
WxUnionID: "wx_unionid_abcdef",
Nickname: "测试用户B",
AvatarURL: "https://example.com/avatar.jpg",
Status: constants.StatusEnabled,
},
wantErr: false,
@@ -74,9 +74,10 @@ func TestPersonalCustomerStore_GetByID(t *testing.T) {
// 创建测试客户
customer := &model.PersonalCustomer{
Phone: "13800000001",
Nickname: "测试客户",
Status: constants.StatusEnabled,
WxOpenID: "wx_openid_test_getbyid",
WxUnionID: "wx_unionid_test_getbyid",
Nickname: "测试客户",
Status: constants.StatusEnabled,
}
err := store.Create(ctx, customer)
require.NoError(t, err)
@@ -84,7 +85,7 @@ func TestPersonalCustomerStore_GetByID(t *testing.T) {
t.Run("查询存在的客户", func(t *testing.T) {
found, err := store.GetByID(ctx, customer.ID)
require.NoError(t, err)
assert.Equal(t, customer.Phone, found.Phone)
assert.Equal(t, customer.WxOpenID, found.WxOpenID)
assert.Equal(t, customer.Nickname, found.Nickname)
})
@@ -104,13 +105,24 @@ func TestPersonalCustomerStore_GetByPhone(t *testing.T) {
// 创建测试客户
customer := &model.PersonalCustomer{
Phone: "13800000001",
Nickname: "测试客户",
Status: constants.StatusEnabled,
WxOpenID: "wx_openid_test_phone",
WxUnionID: "wx_unionid_test_phone",
Nickname: "测试客户",
Status: constants.StatusEnabled,
}
err := store.Create(ctx, customer)
require.NoError(t, err)
// 创建手机号绑定记录
customerPhone := &model.PersonalCustomerPhone{
CustomerID: customer.ID,
Phone: "13800000001",
IsPrimary: true,
Status: constants.StatusEnabled,
}
err = db.Create(customerPhone).Error
require.NoError(t, err)
t.Run("根据手机号查询", func(t *testing.T) {
found, err := store.GetByPhone(ctx, "13800000001")
require.NoError(t, err)
@@ -134,10 +146,9 @@ func TestPersonalCustomerStore_GetByWxOpenID(t *testing.T) {
// 创建测试客户
customer := &model.PersonalCustomer{
Phone: "13800000001",
Nickname: "测试客户",
WxOpenID: "wx_openid_unique",
WxUnionID: "wx_unionid_unique",
Nickname: "测试客户",
Status: constants.StatusEnabled,
}
err := store.Create(ctx, customer)
@@ -147,7 +158,7 @@ func TestPersonalCustomerStore_GetByWxOpenID(t *testing.T) {
found, err := store.GetByWxOpenID(ctx, "wx_openid_unique")
require.NoError(t, err)
assert.Equal(t, customer.ID, found.ID)
assert.Equal(t, customer.Phone, found.Phone)
assert.Equal(t, customer.WxOpenID, found.WxOpenID)
})
t.Run("查询不存在的OpenID", func(t *testing.T) {
@@ -166,9 +177,10 @@ func TestPersonalCustomerStore_Update(t *testing.T) {
// 创建测试客户
customer := &model.PersonalCustomer{
Phone: "13800000001",
Nickname: "原昵称",
Status: constants.StatusEnabled,
WxOpenID: "wx_openid_test_update",
WxUnionID: "wx_unionid_test_update",
Nickname: "原昵称",
Status: constants.StatusEnabled,
}
err := store.Create(ctx, customer)
require.NoError(t, err)
@@ -220,9 +232,10 @@ func TestPersonalCustomerStore_Delete(t *testing.T) {
// 创建测试客户
customer := &model.PersonalCustomer{
Phone: "13800000001",
Nickname: "待删除客户",
Status: constants.StatusEnabled,
WxOpenID: "wx_openid_test_delete",
WxUnionID: "wx_unionid_test_delete",
Nickname: "待删除客户",
Status: constants.StatusEnabled,
}
err := store.Create(ctx, customer)
require.NoError(t, err)
@@ -248,9 +261,10 @@ func TestPersonalCustomerStore_List(t *testing.T) {
// 创建多个测试客户
for i := 1; i <= 5; i++ {
customer := &model.PersonalCustomer{
Phone: testutils.GeneratePhone("138", i),
Nickname: testutils.GenerateUsername("客户", i),
Status: constants.StatusEnabled,
WxOpenID: testutils.GenerateUsername("wx_openid_list_", i),
WxUnionID: testutils.GenerateUsername("wx_unionid_list_", i),
Nickname: testutils.GenerateUsername("客户", i),
Status: constants.StatusEnabled,
}
err := store.Create(ctx, customer)
require.NoError(t, err)
@@ -285,18 +299,20 @@ func TestPersonalCustomerStore_UniqueConstraints(t *testing.T) {
// 创建测试客户
customer := &model.PersonalCustomer{
Phone: "13800000001",
Nickname: "唯一测试客户",
Status: constants.StatusEnabled,
WxOpenID: "wx_openid_unique_test",
WxUnionID: "wx_unionid_unique_test",
Nickname: "唯一测试客户",
Status: constants.StatusEnabled,
}
err := store.Create(ctx, customer)
require.NoError(t, err)
t.Run("重复手机号应失败", func(t *testing.T) {
t.Run("重复微信OpenID应失败", func(t *testing.T) {
duplicate := &model.PersonalCustomer{
Phone: "13800000001", // 重复
Nickname: "另一个客户",
Status: constants.StatusEnabled,
WxOpenID: "wx_openid_unique_test", // 重复
WxUnionID: "wx_unionid_different",
Nickname: "另一个客户",
Status: constants.StatusEnabled,
}
err := store.Create(ctx, duplicate)
assert.Error(t, err)