feat(distribution): 分销站点增加用户数量统计与用户信息展示模块
- 数据模型与迁移: 新增 000082 迁移在 users 表追加 distribution_site_id 并建索引,用户首次在分销站点注册/短信登录时自动绑定站点归属 - 分销管理接口: 新增 GET /admin/distribution/users 接口,聚合本站注册用户与下单客户,返回完整11位手机号、累计消费金额、订单数及来源标签 - 统计与大盘扩展: GET /admin/distribution/sites/:id/analytics 补全今日注册、累计用户、成交客户指标,主站分销商列表追加用户体量展示 - 分销后台前端: 新增「客户与用户」页面 (Users.tsx) 支持一键复制手机号与订单穿透,大盘 (Dashboard.tsx) 增加用户规模核心卡片 - 自动化测试与门禁: 新增 TestListDistributionUsersAndAnalytics 单元测试,通过全量 Go 后端集成测试及前后端构建检查
This commit is contained in:
@@ -69,21 +69,35 @@ func (r *UserRepository) UpdateProfile(ctx context.Context, id uint64, nickname
|
||||
return r.FindByID(ctx, id)
|
||||
}
|
||||
|
||||
func (r *UserRepository) FindOrCreateByPhone(ctx context.Context, phone string, trackCode ...string) (*model.User, error) {
|
||||
func (r *UserRepository) resolveSiteID(ctx context.Context, siteCode string) uint64 {
|
||||
siteCode = strings.TrimSpace(siteCode)
|
||||
if siteCode == "" {
|
||||
return 0
|
||||
}
|
||||
var ds model.DistributionSite
|
||||
if err := r.db.WithContext(ctx).Where("site_code = ? AND status = 'active'", siteCode).Select("id").First(&ds).Error; err == nil {
|
||||
return ds.ID
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (r *UserRepository) FindOrCreateByPhone(ctx context.Context, phone string, trackCode string, siteCode ...string) (*model.User, error) {
|
||||
now := time.Now()
|
||||
var code string
|
||||
if len(trackCode) > 0 {
|
||||
code = sanitizeTrackCode(trackCode[0])
|
||||
code := sanitizeTrackCode(trackCode)
|
||||
var siteID uint64
|
||||
if len(siteCode) > 0 {
|
||||
siteID = r.resolveSiteID(ctx, siteCode[0])
|
||||
}
|
||||
user := model.User{
|
||||
Phone: phone,
|
||||
Nickname: "用户" + phone[len(phone)-4:],
|
||||
RealnameStatus: "unverified",
|
||||
RiskStatus: "normal",
|
||||
CreditScore: 100,
|
||||
Status: "active",
|
||||
RegisterTrackCode: code,
|
||||
LastLoginAt: &now,
|
||||
Phone: phone,
|
||||
Nickname: "用户" + phone[len(phone)-4:],
|
||||
RealnameStatus: "unverified",
|
||||
RiskStatus: "normal",
|
||||
CreditScore: 100,
|
||||
Status: "active",
|
||||
RegisterTrackCode: code,
|
||||
DistributionSiteID: siteID,
|
||||
LastLoginAt: &now,
|
||||
}
|
||||
|
||||
err := r.db.WithContext(ctx).Clauses(clause.OnConflict{
|
||||
@@ -122,22 +136,24 @@ func (r *UserRepository) RevokeTokens(ctx context.Context, userID uint64) error
|
||||
UpdateColumn("token_version", gorm.Expr("token_version + 1")).Error
|
||||
}
|
||||
|
||||
func (r *UserRepository) RegisterWithPassword(ctx context.Context, phone string, hash string, trackCode ...string) (*model.User, error) {
|
||||
func (r *UserRepository) RegisterWithPassword(ctx context.Context, phone string, hash string, trackCode string, siteCode ...string) (*model.User, error) {
|
||||
now := time.Now()
|
||||
var code string
|
||||
if len(trackCode) > 0 {
|
||||
code = sanitizeTrackCode(trackCode[0])
|
||||
code := sanitizeTrackCode(trackCode)
|
||||
var siteID uint64
|
||||
if len(siteCode) > 0 {
|
||||
siteID = r.resolveSiteID(ctx, siteCode[0])
|
||||
}
|
||||
user := model.User{
|
||||
Phone: phone,
|
||||
PasswordHash: hash,
|
||||
Nickname: "用户" + phone[len(phone)-4:],
|
||||
RealnameStatus: "unverified",
|
||||
RiskStatus: "normal",
|
||||
CreditScore: 100,
|
||||
Status: "active",
|
||||
RegisterTrackCode: code,
|
||||
LastLoginAt: &now,
|
||||
Phone: phone,
|
||||
PasswordHash: hash,
|
||||
Nickname: "用户" + phone[len(phone)-4:],
|
||||
RealnameStatus: "unverified",
|
||||
RiskStatus: "normal",
|
||||
CreditScore: 100,
|
||||
Status: "active",
|
||||
RegisterTrackCode: code,
|
||||
DistributionSiteID: siteID,
|
||||
LastLoginAt: &now,
|
||||
}
|
||||
|
||||
err := r.db.WithContext(ctx).Create(&user).Error
|
||||
|
||||
Reference in New Issue
Block a user