236 lines
8.9 KiB
Go
236 lines
8.9 KiB
Go
// 功能描述:实现平台总后台的用户地址与服务关系管理。
|
||
// 版本:v1.1
|
||
package user
|
||
|
||
import (
|
||
"errors"
|
||
|
||
"git.apinb.com/bsm-sdk/core/errcode"
|
||
"git.apinb.com/bsm-sdk/core/infra"
|
||
"git.apinb.com/heqiapp/platforms/backend/api/internal/impl"
|
||
"git.apinb.com/heqiapp/platforms/backend/api/internal/logic/common"
|
||
"git.apinb.com/heqiapp/platforms/backend/api/internal/models"
|
||
"github.com/gin-gonic/gin"
|
||
"gorm.io/gorm"
|
||
)
|
||
|
||
type addressRequest struct {
|
||
UserAccountIdentity string `json:"user_account_identity" binding:"required"`
|
||
Address string `json:"address" binding:"required,max=255"`
|
||
Longitude string `json:"longitude" binding:"max=32"`
|
||
Latitude string `json:"latitude" binding:"max=32"`
|
||
IsDefault bool `json:"is_default"`
|
||
}
|
||
|
||
// ListUserAddress 查询用户地址分页列表,并为已授权的平台页面恢复地址与坐标。
|
||
func ListUserAddress(ctx *gin.Context) {
|
||
page, size := common.PageSize(ctx)
|
||
var list []models.UserAddress
|
||
var total int64
|
||
query := common.ApplyKeywordFilter(ctx, common.ActiveRecords(impl.DBService.Model(&models.UserAddress{})), &models.UserAddress{})
|
||
if err := query.Count(&total).Error; err != nil {
|
||
infra.Response.Error(ctx, err)
|
||
return
|
||
}
|
||
if err := query.Order("created_at desc").Offset((page - 1) * size).Limit(size).Find(&list).Error; err != nil {
|
||
infra.Response.Error(ctx, err)
|
||
return
|
||
}
|
||
response, err := common.PublicResourceResponse(list)
|
||
if err != nil {
|
||
infra.Response.Error(ctx, err)
|
||
return
|
||
}
|
||
protected := common.ProtectPreciseLocation(ctx, &models.UserAddress{}, response)
|
||
infra.Response.Success(ctx, gin.H{"total": total, "list": restoreUserAddressLocations(protected, list)})
|
||
}
|
||
|
||
// GetUserAddress 查询用户地址详情,并返回详情与编辑页需要的地址和坐标。
|
||
func GetUserAddress(ctx *gin.Context) {
|
||
var address models.UserAddress
|
||
if err := common.ActiveRecords(impl.DBService).Where("identity = ?", ctx.Param("identity")).First(&address).Error; err != nil {
|
||
common.RespondRecordError(ctx, err)
|
||
return
|
||
}
|
||
response, err := common.PublicResourceResponse(address)
|
||
if err != nil {
|
||
infra.Response.Error(ctx, err)
|
||
return
|
||
}
|
||
protected := common.ProtectPreciseLocation(ctx, &models.UserAddress{}, response)
|
||
infra.Response.Success(ctx, restoreUserAddressLocations(protected, []models.UserAddress{address}))
|
||
}
|
||
|
||
// restoreUserAddressLocations 仅在用户地址受控接口中恢复完整地址和精确坐标。
|
||
func restoreUserAddressLocations(response any, addresses []models.UserAddress) any {
|
||
restore := func(record map[string]any, address models.UserAddress) {
|
||
record["address"] = address.Address
|
||
record["longitude"] = address.Longitude
|
||
record["latitude"] = address.Latitude
|
||
}
|
||
switch data := response.(type) {
|
||
case []any:
|
||
for index, item := range data {
|
||
if index >= len(addresses) {
|
||
break
|
||
}
|
||
if record, ok := item.(map[string]any); ok {
|
||
restore(record, addresses[index])
|
||
}
|
||
}
|
||
case map[string]any:
|
||
if len(addresses) > 0 {
|
||
restore(data, addresses[0])
|
||
}
|
||
}
|
||
return response
|
||
}
|
||
func CreateUserAddress(ctx *gin.Context) {
|
||
var request addressRequest
|
||
if err := ctx.ShouldBindJSON(&request); err != nil {
|
||
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
|
||
return
|
||
}
|
||
userAccountID, err := common.ResolveIdentityID(&models.UserAccount{}, request.UserAccountIdentity, true)
|
||
if err != nil {
|
||
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
|
||
return
|
||
}
|
||
address := models.UserAddress{Entity: common.NewEntity(common.StatusEnable), UserAccountID: userAccountID, Address: request.Address, Longitude: request.Longitude, Latitude: request.Latitude, IsDefault: request.IsDefault}
|
||
if err := impl.DBService.Transaction(func(tx *gorm.DB) error {
|
||
if request.IsDefault {
|
||
if err := tx.Model(&models.UserAddress{}).Where("user_account_id = ?", userAccountID).Update("is_default", false).Error; err != nil {
|
||
return err
|
||
}
|
||
}
|
||
return tx.Create(&address).Error
|
||
}); err != nil {
|
||
infra.Response.Error(ctx, err)
|
||
return
|
||
}
|
||
common.RespondCreatedResource(ctx, address)
|
||
}
|
||
func UpdateUserAddress(ctx *gin.Context) {
|
||
var request addressRequest
|
||
if err := ctx.ShouldBindJSON(&request); err != nil {
|
||
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
|
||
return
|
||
}
|
||
userAccountID, err := common.ResolveIdentityID(&models.UserAccount{}, request.UserAccountIdentity, true)
|
||
if err != nil {
|
||
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
|
||
return
|
||
}
|
||
err = impl.DBService.Transaction(func(tx *gorm.DB) error {
|
||
var current models.UserAddress
|
||
if err := tx.Where("identity = ?", ctx.Param("identity")).First(¤t).Error; err != nil {
|
||
return err
|
||
}
|
||
if current.UserAccountID != userAccountID {
|
||
return errors.New("address owner cannot be changed")
|
||
}
|
||
if request.IsDefault {
|
||
if err := tx.Model(&models.UserAddress{}).
|
||
Where("user_account_id = ? AND id <> ?", userAccountID, current.ID).
|
||
Update("is_default", false).Error; err != nil {
|
||
return err
|
||
}
|
||
}
|
||
return tx.Model(¤t).Updates(userAddressUpdateValues(request, userAccountID)).Error
|
||
})
|
||
if err != nil {
|
||
common.RespondRecordError(ctx, err)
|
||
return
|
||
}
|
||
infra.Response.Success(ctx, gin.H{"updated": true})
|
||
}
|
||
|
||
// userAddressUpdateValues 构造 GORM 支持的标准更新映射,并保留 false 等零值字段。
|
||
func userAddressUpdateValues(request addressRequest, userAccountID uint64) map[string]any {
|
||
return map[string]any{
|
||
"user_account_id": userAccountID,
|
||
"address": request.Address,
|
||
"longitude": request.Longitude,
|
||
"latitude": request.Latitude,
|
||
"is_default": request.IsDefault,
|
||
}
|
||
}
|
||
|
||
type serviceRelationRequest struct {
|
||
UserAccountIdentity string `json:"user_account_identity" binding:"required"`
|
||
GasBasicIdentity string `json:"gas_basic_identity"`
|
||
DeliveryBasicIdentity string `json:"delivery_basic_identity"`
|
||
StaffAccountIdentity string `json:"staff_account_identity"`
|
||
}
|
||
|
||
func ListUserServiceRelation(ctx *gin.Context) { common.ListPage[models.UserServiceRelation](ctx) }
|
||
func GetUserServiceRelation(ctx *gin.Context) { common.GetByIdentity[models.UserServiceRelation](ctx) }
|
||
func CreateUserServiceRelation(ctx *gin.Context) {
|
||
var request serviceRelationRequest
|
||
if err := ctx.ShouldBindJSON(&request); err != nil {
|
||
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
|
||
return
|
||
}
|
||
userAccountID, gasBasicID, deliveryBasicID, staffAccountID, ok := resolveServiceRelation(ctx, request)
|
||
if !ok {
|
||
return
|
||
}
|
||
relation := models.UserServiceRelation{Entity: common.NewEntity(common.StatusEnable), UserAccountID: userAccountID, GasBasicID: gasBasicID, DeliveryBasicID: deliveryBasicID, StaffAccountID: staffAccountID}
|
||
if err := impl.DBService.Create(&relation).Error; err != nil {
|
||
infra.Response.Error(ctx, err)
|
||
return
|
||
}
|
||
common.RespondCreatedResource(ctx, relation)
|
||
}
|
||
func UpdateUserServiceRelation(ctx *gin.Context) {
|
||
var request serviceRelationRequest
|
||
if err := ctx.ShouldBindJSON(&request); err != nil {
|
||
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
|
||
return
|
||
}
|
||
userAccountID, gasBasicID, deliveryBasicID, staffAccountID, ok := resolveServiceRelation(ctx, request)
|
||
if !ok {
|
||
return
|
||
}
|
||
var current models.UserServiceRelation
|
||
if err := common.ActiveRecords(impl.DBService).Select("user_account_id").
|
||
Where("identity = ?", ctx.Param("identity")).First(¤t).Error; err != nil {
|
||
common.RespondRecordError(ctx, err)
|
||
return
|
||
}
|
||
if current.UserAccountID != userAccountID {
|
||
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
|
||
return
|
||
}
|
||
common.UpdateAllowedByIdentity(ctx, &models.UserServiceRelation{}, gin.H{"user_account_id": userAccountID, "gas_basic_id": gasBasicID, "delivery_basic_id": deliveryBasicID, "staff_account_id": staffAccountID}, []string{"user_account_id", "gas_basic_id", "delivery_basic_id", "staff_account_id"})
|
||
}
|
||
|
||
func resolveServiceRelation(ctx *gin.Context, request serviceRelationRequest) (uint64, uint64, uint64, uint64, bool) {
|
||
userAccountID, err := common.ResolveIdentityID(&models.UserAccount{}, request.UserAccountIdentity, true)
|
||
if err != nil {
|
||
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
|
||
return 0, 0, 0, 0, false
|
||
}
|
||
gasBasicID, err := common.ResolveIdentityID(&models.GasBasic{}, request.GasBasicIdentity, false)
|
||
if err != nil {
|
||
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
|
||
return 0, 0, 0, 0, false
|
||
}
|
||
deliveryBasicID, err := common.ResolveIdentityID(&models.DeliveryBasic{}, request.DeliveryBasicIdentity, false)
|
||
if err != nil {
|
||
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
|
||
return 0, 0, 0, 0, false
|
||
}
|
||
staffAccountID, err := common.ResolveIdentityID(&models.StaffAccount{}, request.StaffAccountIdentity, false)
|
||
if err != nil {
|
||
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
|
||
return 0, 0, 0, 0, false
|
||
}
|
||
if gasBasicID == 0 && deliveryBasicID == 0 && staffAccountID == 0 ||
|
||
!common.ValidateOrganizationIDs(gasBasicID, deliveryBasicID, staffAccountID) {
|
||
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
|
||
return 0, 0, 0, 0, false
|
||
}
|
||
return userAccountID, gasBasicID, deliveryBasicID, staffAccountID, true
|
||
}
|