package userclient import ( "context" userv1 "hyapp.local/api/proto/user/v1" ) // Country 是 admin-server 暴露给后台前端的国家主数据 DTO。 // 这里不引用 user-service 的 domain/storage 包,避免后台服务和用户服务代码结构耦合。 type Country struct { // CountryID 是 user-service 生成的国家主键,后台增删改查都以它定位记录。 CountryID int64 `json:"countryId"` // CountryName 是稳定英文或拼音名称,例如 China、United States。 CountryName string `json:"countryName"` // CountryCode 是 App 和用户链路使用的 canonical 国家码,创建后不可变。 CountryCode string `json:"countryCode"` // CountryDisplayName 是后台和 App 注册页展示用中文名。 CountryDisplayName string `json:"countryDisplayName"` // SortOrder 控制 App 注册页和后台列表的默认排序。 SortOrder int32 `json:"sortOrder"` // CreatedAtMs 直接透传 user-service 的毫秒时间戳,避免跨服务时区转换。 CreatedAtMs int64 `json:"createdAtMs"` // UpdatedAtMs 直接透传 user-service 的毫秒时间戳,便于后台做新旧数据判断。 UpdatedAtMs int64 `json:"updatedAtMs"` // ISOAlpha3 是 ISO 3166-1 alpha-3 或明确的 user-assigned code。 ISOAlpha3 string `json:"isoAlpha3"` // ISONumeric 必须保持字符串,避免 020 这类前导零被破坏。 ISONumeric string `json:"isoNumeric"` // PhoneCountryCode 是 E.164 国家呼叫码,例如 +86。 PhoneCountryCode string `json:"phoneCountryCode"` // Enabled 表示该国家是否允许 App 注册和用户改国家选择。 Enabled bool `json:"enabled"` // Flag 是展示字段,不参与国家唯一性、区域归属或权限判断。 Flag string `json:"flag"` } // Region 是 user-service 区域主数据投影;countries 只包含当前 active 国家码。 type Region struct { // RegionID 是区域主键,BD Leader/BD/Agency 后续都围绕它建立组织关系。 RegionID int64 `json:"regionId"` // RegionCode 是稳定业务编码;后台修改时仍由 user-service 做唯一性校验。 RegionCode string `json:"regionCode"` // Name 是后台展示名。 Name string `json:"name"` // Status 当前只由 user-service 约束为 active 或 disabled。 Status string `json:"status"` // Countries 是 active 映射国家码列表;一个国家不能同时属于多个 active 区域。 Countries []string `json:"countries"` // SortOrder 控制后台和业务选择器排序。 SortOrder int32 `json:"sortOrder"` // CreatedAtMs 直接透传 user-service 的毫秒时间戳。 CreatedAtMs int64 `json:"createdAtMs"` // UpdatedAtMs 直接透传 user-service 的毫秒时间戳。 UpdatedAtMs int64 `json:"updatedAtMs"` } // ListCountriesRequest 支持 enabled 三态过滤:nil 表示全部,true/false 表示显式筛选。 type ListCountriesRequest struct { RequestID string Caller string Enabled *bool } // UpdateCountryRequest 只允许修改展示和辅助字段,不允许修改 country_code。 type UpdateCountryRequest struct { RequestID string Caller string CountryID int64 CountryName string CountryDisplayName string SortOrder int32 OperatorUserID int64 ISOAlpha3 string ISONumeric string PhoneCountryCode string Flag string } // ListRegionsRequest 支持 status 过滤;空字符串表示全部。 type ListRegionsRequest struct { RequestID string Caller string Status string } // GetRegionRequest 按区域主键查询单个区域及 active 国家码。 type GetRegionRequest struct { RequestID string Caller string RegionID int64 } // UpdateRegionRequest 只修改区域展示字段,不替换国家归属。 type UpdateRegionRequest struct { RequestID string Caller string RegionID int64 RegionCode string Name string SortOrder int32 OperatorUserID int64 } // ReplaceRegionCountriesRequest 原子替换区域国家归属,并由 user-service 创建用户区域重算任务。 type ReplaceRegionCountriesRequest struct { RequestID string Caller string RegionID int64 Countries []string OperatorUserID int64 } func (c *GRPCClient) ListCountries(ctx context.Context, req ListCountriesRequest) ([]*Country, error) { resp, err := c.countryClient.ListCountries(ctx, &userv1.ListCountriesRequest{ Meta: requestMeta(ctx, req.RequestID, req.Caller), Enabled: req.Enabled, }) if err != nil { return nil, err } items := make([]*Country, 0, len(resp.GetCountries())) for _, country := range resp.GetCountries() { items = append(items, fromProtoCountry(country)) } return items, nil } func (c *GRPCClient) UpdateCountry(ctx context.Context, req UpdateCountryRequest) (*Country, error) { resp, err := c.countryClient.UpdateCountry(ctx, &userv1.UpdateCountryRequest{ Meta: requestMeta(ctx, req.RequestID, req.Caller), CountryId: req.CountryID, CountryName: req.CountryName, CountryDisplayName: req.CountryDisplayName, SortOrder: req.SortOrder, OperatorUserId: req.OperatorUserID, IsoAlpha3: req.ISOAlpha3, IsoNumeric: req.ISONumeric, PhoneCountryCode: req.PhoneCountryCode, Flag: req.Flag, }) if err != nil { return nil, err } return fromProtoCountry(resp.GetCountry()), nil } func (c *GRPCClient) ListRegions(ctx context.Context, req ListRegionsRequest) ([]*Region, error) { resp, err := c.regionClient.ListRegions(ctx, &userv1.ListRegionsRequest{ Meta: requestMeta(ctx, req.RequestID, req.Caller), Status: req.Status, }) if err != nil { return nil, err } items := make([]*Region, 0, len(resp.GetRegions())) for _, region := range resp.GetRegions() { items = append(items, fromProtoRegion(region)) } return items, nil } func (c *GRPCClient) GetRegion(ctx context.Context, req GetRegionRequest) (*Region, error) { resp, err := c.regionClient.GetRegion(ctx, &userv1.GetRegionRequest{ Meta: requestMeta(ctx, req.RequestID, req.Caller), RegionId: req.RegionID, }) if err != nil { return nil, err } return fromProtoRegion(resp.GetRegion()), nil } func (c *GRPCClient) UpdateRegion(ctx context.Context, req UpdateRegionRequest) (*Region, error) { resp, err := c.regionClient.UpdateRegion(ctx, &userv1.UpdateRegionRequest{ Meta: requestMeta(ctx, req.RequestID, req.Caller), RegionId: req.RegionID, RegionCode: req.RegionCode, Name: req.Name, SortOrder: req.SortOrder, OperatorUserId: req.OperatorUserID, }) if err != nil { return nil, err } return fromProtoRegion(resp.GetRegion()), nil } func (c *GRPCClient) ReplaceRegionCountries(ctx context.Context, req ReplaceRegionCountriesRequest) (*Region, error) { resp, err := c.regionClient.ReplaceRegionCountries(ctx, &userv1.ReplaceRegionCountriesRequest{ Meta: requestMeta(ctx, req.RequestID, req.Caller), RegionId: req.RegionID, Countries: append([]string(nil), req.Countries...), OperatorUserId: req.OperatorUserID, }) if err != nil { return nil, err } return fromProtoRegion(resp.GetRegion()), nil } func fromProtoCountry(country *userv1.Country) *Country { if country == nil { return nil } return &Country{ CountryID: country.GetCountryId(), CountryName: country.GetCountryName(), CountryCode: country.GetCountryCode(), CountryDisplayName: country.GetCountryDisplayName(), SortOrder: country.GetSortOrder(), CreatedAtMs: country.GetCreatedAtMs(), UpdatedAtMs: country.GetUpdatedAtMs(), ISOAlpha3: country.GetIsoAlpha3(), ISONumeric: country.GetIsoNumeric(), PhoneCountryCode: country.GetPhoneCountryCode(), Enabled: country.GetEnabled(), Flag: country.GetFlag(), } } func fromProtoRegion(region *userv1.Region) *Region { if region == nil { return nil } return &Region{ RegionID: region.GetRegionId(), RegionCode: region.GetRegionCode(), Name: region.GetName(), Status: region.GetStatus(), Countries: append([]string{}, region.GetCountries()...), SortOrder: region.GetSortOrder(), CreatedAtMs: region.GetCreatedAtMs(), UpdatedAtMs: region.GetUpdatedAtMs(), } }