feat(distribution): 支持分站发布群固定二维码

主站分销配置可为每个站点绑定可复用企微码,号主在该站上传账号后发到对应发布群,不再消耗主站码池。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
yml2213
2026-09-15 19:02:10 +08:00
co-authored by Cursor
parent 776c33f83c
commit 1c86bef765
9 changed files with 397 additions and 16 deletions
@@ -15,6 +15,7 @@ type SiteDTO struct {
AnnouncementsJSON string `json:"announcements_json"`
FooterInfoJSON string `json:"footer_info_json"`
DefaultSupportGroupID *uint64 `json:"default_support_group_id,omitempty"`
ListingQrcodeURL string `json:"listing_qrcode_url"`
TaxEnabled bool `json:"tax_enabled"`
TaxRateBPS int64 `json:"tax_rate_bps"`
SettlementHoldDays int `json:"settlement_hold_days"`
@@ -28,6 +29,7 @@ type CreateSiteRequest struct {
Domain string `json:"domain" binding:"required,max=255"`
DisplayAccountEnabled *bool `json:"display_account_enabled"`
DefaultSupportGroupID *uint64 `json:"default_support_group_id"`
ListingQrcodeURL *string `json:"listing_qrcode_url"`
TaxEnabled *bool `json:"tax_enabled"`
TaxRateBPS *int64 `json:"tax_rate_bps"`
SettlementHoldDays *int `json:"settlement_hold_days"`
@@ -39,6 +41,7 @@ type UpdateSiteRequest struct {
Status *string `json:"status"`
DisplayAccountEnabled *bool `json:"display_account_enabled"`
DefaultSupportGroupID *uint64 `json:"default_support_group_id"`
ListingQrcodeURL *string `json:"listing_qrcode_url"`
TaxEnabled *bool `json:"tax_enabled"`
TaxRateBPS *int64 `json:"tax_rate_bps"`
SettlementHoldDays *int `json:"settlement_hold_days"`
@@ -253,6 +253,9 @@ func (r *Repository) CreateSite(ctx context.Context, req CreateSiteRequest) (*Si
if req.DefaultSupportGroupID != nil {
row.DefaultSupportGroupID = req.DefaultSupportGroupID
}
if req.ListingQrcodeURL != nil {
row.ListingQrcodeURL = strings.TrimSpace(*req.ListingQrcodeURL)
}
if req.TaxEnabled != nil {
row.TaxEnabled = *req.TaxEnabled
}
@@ -305,6 +308,9 @@ func (r *Repository) UpdateSite(ctx context.Context, adminID, id uint64, req Upd
if req.DefaultSupportGroupID != nil {
row.DefaultSupportGroupID = req.DefaultSupportGroupID
}
if req.ListingQrcodeURL != nil {
row.ListingQrcodeURL = strings.TrimSpace(*req.ListingQrcodeURL)
}
if req.TaxEnabled != nil {
row.TaxEnabled = *req.TaxEnabled
}
@@ -467,6 +473,7 @@ func siteDTO(row model.DistributionSite) SiteDTO {
Status: row.Status,
DisplayAccountEnabled: row.DisplayAccountEnabled,
DefaultSupportGroupID: row.DefaultSupportGroupID,
ListingQrcodeURL: row.ListingQrcodeURL,
TaxEnabled: row.TaxEnabled,
TaxRateBPS: row.TaxRateBPS,
SettlementHoldDays: row.SettlementHoldDays,
@@ -649,6 +649,23 @@ func TestDistributionBrandingAndMarkup(t *testing.T) {
t.Fatalf("unexpected branding: %+v", updated)
}
qrURL := "/api/files/object?key=qrcode/brand-site.png"
qrUpdated, err := repo.UpdateSite(t.Context(), 0, site.ID, UpdateSiteRequest{ListingQrcodeURL: &qrURL})
if err != nil {
t.Fatalf("UpdateSite listing qrcode failed: %v", err)
}
if qrUpdated.ListingQrcodeURL != qrURL {
t.Fatalf("listing_qrcode_url = %q, want %q", qrUpdated.ListingQrcodeURL, qrURL)
}
cleared := ""
qrCleared, err := repo.UpdateSite(t.Context(), 0, site.ID, UpdateSiteRequest{ListingQrcodeURL: &cleared})
if err != nil {
t.Fatalf("clear listing qrcode failed: %v", err)
}
if qrCleared.ListingQrcodeURL != "" {
t.Fatalf("cleared listing_qrcode_url = %q, want empty", qrCleared.ListingQrcodeURL)
}
// Verify PublicSiteInfo contains branding
pubInfo, err := repo.GetPublicSiteInfo(t.Context(), "brand_site", "")
if err != nil {