Files
platforms/backend/api/internal/logic/gas/user.go

379 lines
12 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package gas
import (
"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/logic/upload"
"git.apinb.com/heqiapp/platforms/backend/api/internal/models"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
func ListUser(ctx *gin.Context) {
station, ok := currentGas(ctx)
if !ok {
return
}
page, size := common.PageSize(ctx)
var list []models.UserAccount
var total int64
query := common.ApplyKeywordFilter(ctx,
common.ActiveRecords(impl.DBService.Model(&models.UserAccount{})).
Joins("JOIN user_service_relation ON user_service_relation.user_account_id = user_account.id AND user_service_relation.status <> ?",
common.StatusArchived).
Where("user_service_relation.gas_basic_id = ?", station.ID),
&models.UserAccount{})
if err := query.Count(&total).Error; err != nil {
infra.Response.Error(ctx, err)
return
}
if err := query.Order("user_account.created_at desc").Offset((page - 1) * size).Limit(size).Find(&list).Error; err != nil {
infra.Response.Error(ctx, err)
return
}
respondList(ctx, list, total)
}
func GetUser(ctx *gin.Context) {
station, ok := currentGas(ctx)
if !ok {
return
}
user, relation, ok := requireUser(ctx, ctx.Param("identity"), station.ID)
if !ok {
return
}
var addresses []models.UserAddress
if err := common.ActiveRecords(impl.DBService).Where("user_account_id = ?", user.ID).Order("is_default desc, created_at desc").Find(&addresses).Error; err != nil {
infra.Response.Error(ctx, err)
return
}
response, err := common.PublicResourceResponse(gin.H{"user": user, "relation": relation, "addresses": addresses})
if err != nil {
infra.Response.Error(ctx, err)
return
}
infra.Response.Success(ctx, response)
}
// GetUserAvatar 返回与当前气站存在服务关系的用户受保护头像。
func GetUserAvatar(ctx *gin.Context) {
station, ok := currentGas(ctx)
if !ok {
return
}
user, _, ok := requireUser(ctx, ctx.Param("identity"), station.ID)
if !ok {
return
}
upload.ServeAvatar(ctx, user.Avatar)
}
type userRequest struct {
Username string `json:"username"`
Password string `json:"password"`
Name string `json:"name" binding:"required,max=64"`
Phone string `json:"phone" binding:"max=32"`
Avatar *string `json:"avatar" binding:"omitempty,max=512"`
RealName string `json:"real_name" binding:"max=64"`
DeliveryBasicIdentity string `json:"delivery_basic_identity" binding:"required"`
}
func CreateUser(ctx *gin.Context) {
station, ok := currentGas(ctx)
if !ok {
return
}
var request userRequest
if err := ctx.ShouldBindJSON(&request); err != nil || request.Username == "" || !common.IsValidAccountPassword(request.Password) {
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
return
}
delivery, ok := requireDelivery(ctx, request.DeliveryBasicIdentity, station.ID)
if !ok {
return
}
hash, err := common.PasswordHash(request.Password)
if err != nil {
infra.Response.Error(ctx, err)
return
}
avatar := ""
if request.Avatar != nil {
avatar = *request.Avatar
}
user := models.UserAccount{
Entity: common.NewEntity(common.StatusEnable), Username: request.Username, PasswordHash: hash,
Name: request.Name, Phone: request.Phone, Avatar: avatar, RealName: request.RealName,
}
relation := models.UserServiceRelation{Entity: common.NewEntity(common.StatusEnable), GasBasicID: station.ID, DeliveryBasicID: delivery.ID}
if err := impl.DBService.Transaction(func(tx *gorm.DB) error {
if err := tx.Create(&user).Error; err != nil {
return err
}
relation.UserAccountID = user.ID
return tx.Create(&relation).Error
}); err != nil {
infra.Response.Error(ctx, err)
return
}
common.RespondCreatedResource(ctx, user)
}
func UpdateUser(ctx *gin.Context) {
station, ok := currentGas(ctx)
if !ok {
return
}
user, relation, ok := requireUser(ctx, ctx.Param("identity"), station.ID)
if !ok {
return
}
var request userRequest
if err := ctx.ShouldBindJSON(&request); err != nil {
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
return
}
delivery, ok := requireDelivery(ctx, request.DeliveryBasicIdentity, station.ID)
if !ok {
return
}
if err := impl.DBService.Transaction(func(tx *gorm.DB) error {
values := map[string]any{
"name": request.Name, "phone": request.Phone, "real_name": request.RealName,
}
// 未选择新头像时不提交 avatar避免编辑基础资料误清空现有头像。
if request.Avatar != nil {
values["avatar"] = *request.Avatar
}
if err := tx.Model(&user).Updates(values).Error; err != nil {
return err
}
return tx.Model(&relation).Update("delivery_basic_id", delivery.ID).Error
}); err != nil {
infra.Response.Error(ctx, err)
return
}
infra.Response.Success(ctx, gin.H{"updated": true})
}
func UpdateUserStatus(ctx *gin.Context) {
station, ok := currentGas(ctx)
if !ok {
return
}
if _, _, ok := requireUser(ctx, ctx.Param("identity"), station.ID); !ok {
return
}
common.UpdateRecordStatus(ctx, &models.UserAccount{})
}
func ArchiveUser(ctx *gin.Context) {
station, ok := currentGas(ctx)
if !ok {
return
}
user, relation, ok := requireUser(ctx, ctx.Param("identity"), station.ID)
if !ok {
return
}
var blocking int64
if err := impl.DBService.Model(&models.GasorderBasic{}).
Where("user_account_id = ? AND order_status NOT IN ?", user.ID, []int{common.StatusCompleted, common.StatusCancelled}).
Count(&blocking).Error; err != nil || blocking > 0 {
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
return
}
if err := impl.DBService.Model(&models.CsTicket{}).
Where("user_account_id = ? AND status <> ? AND ticket_status = ?", user.ID, common.StatusArchived, common.StatusOpen).
Count(&blocking).Error; err != nil || blocking > 0 {
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
return
}
var wallet models.WalletBasic
err := impl.DBService.Where("owner_type = ? AND owner_id = ? AND status <> ?", "user", user.ID, common.StatusArchived).First(&wallet).Error
if err == nil && (wallet.Balance > 0 || wallet.WithdrawalBalance > 0) {
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
return
}
if err == nil {
if cashErr := impl.DBService.Model(&models.WalletApplyCash{}).
Where("wallet_basic_id = ? AND status <> ? AND apply_status = ?", wallet.ID, common.StatusArchived, common.StatusPending).
Count(&blocking).Error; cashErr != nil || blocking > 0 {
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
return
}
}
if err != nil && err != gorm.ErrRecordNotFound {
infra.Response.Error(ctx, err)
return
}
if err := impl.DBService.Transaction(func(tx *gorm.DB) error {
if err := tx.Model(&relation).Update("status", common.StatusArchived).Error; err != nil {
return err
}
if err := tx.Model(&models.UserAddress{}).Where("user_account_id = ? AND status <> ?", user.ID, common.StatusArchived).
Update("status", common.StatusArchived).Error; err != nil {
return err
}
return tx.Model(&user).Update("status", common.StatusArchived).Error
}); err != nil {
infra.Response.Error(ctx, err)
return
}
infra.Response.Success(ctx, gin.H{"archived": true})
}
func ListUserAddress(ctx *gin.Context) {
station, ok := currentGas(ctx)
if !ok {
return
}
page, size := common.PageSize(ctx)
var list []models.UserAddress
var total int64
query := common.ActiveRecords(impl.DBService.Model(&models.UserAddress{})).
Joins("JOIN user_service_relation ON user_service_relation.user_account_id = user_address.user_account_id AND user_service_relation.status <> ?",
common.StatusArchived).
Where("user_service_relation.gas_basic_id = ?", station.ID)
if err := query.Count(&total).Error; err != nil {
infra.Response.Error(ctx, err)
return
}
if err := query.Order("user_address.created_at desc").Offset((page - 1) * size).Limit(size).Find(&list).Error; err != nil {
infra.Response.Error(ctx, err)
return
}
respondList(ctx, list, total)
}
func GetUserAddress(ctx *gin.Context) {
station, ok := currentGas(ctx)
if !ok {
return
}
var address models.UserAddress
query := common.ActiveRecords(impl.DBService.Model(&models.UserAddress{})).
Select("user_address.*").
Joins("JOIN user_service_relation ON user_service_relation.user_account_id = user_address.user_account_id AND user_service_relation.status <> ?",
common.StatusArchived).
Where("user_address.identity = ? AND user_service_relation.gas_basic_id = ?", ctx.Param("identity"), station.ID)
respondScopedRecord(ctx, query, &address)
}
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"`
}
func CreateUserAddress(ctx *gin.Context) {
station, ok := currentGas(ctx)
if !ok {
return
}
var request addressRequest
if err := ctx.ShouldBindJSON(&request); err != nil {
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
return
}
user, _, ok := requireUser(ctx, request.UserAccountIdentity, station.ID)
if !ok {
return
}
address := models.UserAddress{
Entity: common.NewEntity(common.StatusEnable), UserAccountID: user.ID, 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 = ?", user.ID).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) {
station, ok := currentGas(ctx)
if !ok {
return
}
var existing models.UserAddress
if err := common.ActiveRecords(impl.DBService.Model(&models.UserAddress{})).
Select("user_address.*").
Joins("JOIN user_service_relation ON user_service_relation.user_account_id = user_address.user_account_id AND user_service_relation.status <> ?",
common.StatusArchived).
Where("user_address.identity = ? AND user_service_relation.gas_basic_id = ?", ctx.Param("identity"), station.ID).
First(&existing).Error; err != nil {
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
return
}
var request addressRequest
if err := ctx.ShouldBindJSON(&request); err != nil {
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
return
}
user, _, ok := requireUser(ctx, request.UserAccountIdentity, station.ID)
if !ok || user.ID != existing.UserAccountID {
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
return
}
if err := impl.DBService.Transaction(func(tx *gorm.DB) error {
if request.IsDefault {
if err := tx.Model(&models.UserAddress{}).Where("user_account_id = ? AND id <> ?", user.ID, existing.ID).
Update("is_default", false).Error; err != nil {
return err
}
}
return tx.Model(&existing).Updates(map[string]any{
"address": request.Address, "longitude": request.Longitude, "latitude": request.Latitude, "is_default": request.IsDefault,
}).Error
}); err != nil {
infra.Response.Error(ctx, err)
return
}
infra.Response.Success(ctx, gin.H{"updated": true})
}
func UpdateUserAddressStatus(ctx *gin.Context) {
if !requireScopedAddress(ctx) {
return
}
common.UpdateRecordStatus(ctx, &models.UserAddress{})
}
func ArchiveUserAddress(ctx *gin.Context) {
if !requireScopedAddress(ctx) {
return
}
common.ArchiveRecord(ctx, &models.UserAddress{})
}
func requireScopedAddress(ctx *gin.Context) bool {
station, ok := currentGas(ctx)
if !ok {
return false
}
var count int64
err := impl.DBService.Model(&models.UserAddress{}).
Joins("JOIN user_service_relation ON user_service_relation.user_account_id = user_address.user_account_id AND user_service_relation.status <> ?",
common.StatusArchived).
Where("user_address.identity = ? AND user_service_relation.gas_basic_id = ?", ctx.Param("identity"), station.ID).
Count(&count).Error
if err != nil || count != 1 {
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
return false
}
return true
}