fix: audit platform contracts and relation identities

This commit is contained in:
2026-07-27 10:25:04 +08:00
parent 2e62238416
commit 7ebe25fe34
9 changed files with 225 additions and 1251 deletions

View File

@@ -102,7 +102,7 @@ func listResource(ctx *gin.Context, model any) {
infra.Response.Error(ctx, err)
return
}
infra.Response.Success(ctx, gin.H{"total": total, "list": resourceResponse(list.Elem().Interface())})
infra.Response.Success(ctx, gin.H{"total": total, "list": publicResourceResponse(list.Elem().Interface())})
}
func getResource(ctx *gin.Context, model any) {
@@ -111,7 +111,7 @@ func getResource(ctx *gin.Context, model any) {
respondRecordError(ctx, err)
return
}
infra.Response.Success(ctx, resourceResponse(data.Interface()))
infra.Response.Success(ctx, publicResourceResponse(data.Interface()))
}
func createResource(ctx *gin.Context, model any, allowedFields []string, relations []ResourceRelation) {
@@ -230,6 +230,113 @@ func resourceResponse(value any) any {
return stripInternalIDs(decoded)
}
// publicResourceResponse additionally resolves persisted relation keys into
// their public identities. It is used by list/detail endpoints so an edit form
// can round-trip the relation without ever receiving a surrogate database ID.
func publicResourceResponse(value any) any {
encoded, err := json.Marshal(value)
if err != nil {
return value
}
var decoded any
if err := json.Unmarshal(encoded, &decoded); err != nil {
return value
}
return projectRelationIdentities(decoded)
}
var relationIdentityModels = map[string]any{
"gas_basic_id": &models.GasBasic{},
"gas_station_id": &models.GasBasic{},
"delivery_basic_id": &models.DeliveryBasic{},
"delivery_point_id": &models.DeliveryBasic{},
"user_account_id": &models.UserAccount{},
"staff_account_id": &models.StaffAccount{},
"smart_cylinder_valve_id": &models.DevSmartCylinderValve{},
"ec_category_id": &models.EcCategory{},
"ec_product_id": &models.EcProduct{},
"ec_order_id": &models.EcOrder{},
"delivery_task_id": &models.DeliveryTask{},
"delivery_track_id": &models.DeliveryTrack{},
"platform_role_id": &models.PlatformRole{},
"platform_menu_id": &models.PlatformMenu{},
"report_id": &models.Report{},
"wallet_id": &models.Wallet{},
}
var relationIdentityKeys = map[string]string{
"gas_station_id": "gas_basic_identity",
"delivery_point_id": "delivery_basic_identity",
}
func projectRelationIdentities(value any) any {
switch data := value.(type) {
case map[string]any:
for key, item := range data {
if key == "id" {
delete(data, key)
continue
}
if strings.HasSuffix(key, "_id") {
identityKey := strings.TrimSuffix(key, "_id") + "_identity"
if alias := relationIdentityKeys[key]; alias != "" {
identityKey = alias
}
model := relationIdentityModels[key]
if key == "subject_id" {
model = settlementSubjectModel(data["subject_type"])
identityKey = "subject_identity"
}
if model != nil {
data[identityKey] = relationIdentity(item, model)
}
delete(data, key)
continue
}
data[key] = projectRelationIdentities(item)
}
case []any:
for index := range data {
data[index] = projectRelationIdentities(data[index])
}
}
return value
}
func relationIdentity(value any, model any) string {
var id uint64
switch raw := value.(type) {
case float64:
id = uint64(raw)
case uint64:
id = raw
case int:
id = uint64(raw)
}
if id == 0 {
return ""
}
var related struct{ Identity string }
if err := impl.DBService.Model(model).Select("identity").Where("id = ?", id).First(&related).Error; err != nil {
return ""
}
return related.Identity
}
func settlementSubjectModel(value any) any {
subjectType, _ := value.(string)
switch subjectType {
case "gas", "gas_basic":
return &models.GasBasic{}
case "delivery", "delivery_basic":
return &models.DeliveryBasic{}
case "staff", "staff_account":
return &models.StaffAccount{}
default:
return nil
}
}
func stripInternalIDs(value any) any {
switch data := value.(type) {
case map[string]any:
@@ -311,7 +418,7 @@ func GetEcOrder(ctx *gin.Context) {
infra.Response.Error(ctx, err)
return
}
infra.Response.Success(ctx, resourceResponse(gin.H{"order": order, "items": items}))
infra.Response.Success(ctx, publicResourceResponse(gin.H{"order": order, "items": items}))
}
// GetDeliveryTrack returns time-ordered points. Precise coordinates are only
@@ -337,7 +444,7 @@ func GetDeliveryTrack(ctx *gin.Context) {
points[index].Latitude = ""
}
}
infra.Response.Success(ctx, resourceResponse(gin.H{"track": track, "points": points}))
infra.Response.Success(ctx, publicResourceResponse(gin.H{"track": track, "points": points}))
}
type ecCategoryView struct {