Files
platforms/backend/api/internal/logic/delivery/staff.go
2026-08-22 22:23:35 +08:00

298 lines
9.1 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 delivery
import (
"time"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/infra"
"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 scopedStaff(ctx *gin.Context, identity string, point models.DeliveryBasic) (models.StaffAccount, bool) {
var staff models.StaffAccount
if err := deliveryStaffQuery(db(), point).Where("identity = ?", identity).First(&staff).Error; err != nil {
common.RespondRecordError(ctx, err)
return staff, false
}
return staff, true
}
// deliveryStaffQuery 固定配送人员的气站、配送点及角色范围。
func deliveryStaffQuery(databaseService *gorm.DB, point models.DeliveryBasic) *gorm.DB {
return common.ActiveRecords(databaseService.Model(&models.StaffAccount{})).
Where("gas_basic_id = ? AND delivery_basic_id = ? AND role_code = ?",
point.GasBasicID, point.ID, "delivery")
}
func ListStaff(ctx *gin.Context) {
point, _, ok := currentScope(ctx)
if !ok {
return
}
query := deliveryStaffQuery(db(), point)
listScoped(ctx, &models.StaffAccount{}, query, "staff_account.created_at desc")
}
func GetStaff(ctx *gin.Context) {
point, _, ok := currentScope(ctx)
if !ok {
return
}
var staff models.StaffAccount
respondRecord(ctx, deliveryStaffQuery(db(), point).Where("identity = ?", ctx.Param("identity")), &staff)
}
// GetStaffAvatar 返回当前配送点范围内配送人员的受保护头像。
func GetStaffAvatar(ctx *gin.Context) {
point, _, ok := currentScope(ctx)
if !ok {
return
}
staff, ok := scopedStaff(ctx, ctx.Param("identity"), point)
if !ok {
return
}
upload.ServeAvatar(ctx, staff.Avatar)
}
type staffRequest 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"`
WorkStatus string `json:"work_status" binding:"required"`
}
func CreateStaff(ctx *gin.Context) {
point, _, ok := currentScope(ctx)
if !ok {
return
}
var request staffRequest
if err := ctx.ShouldBindJSON(&request); err != nil || request.Username == "" ||
!common.IsValidAccountPassword(request.Password) || (request.WorkStatus != "on_duty" && request.WorkStatus != "off_duty") {
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
return
}
hash, err := common.PasswordHash(request.Password)
if err != nil {
infra.Response.Error(ctx, err)
return
}
avatar := ""
if request.Avatar != nil {
avatar = *request.Avatar
}
staff := models.StaffAccount{
Entity: common.NewEntity(common.StatusEnable), Username: request.Username, PasswordHash: hash,
Name: request.Name, Phone: request.Phone, Avatar: avatar, RoleCode: "delivery",
GasBasicID: point.GasBasicID, DeliveryBasicID: point.ID, WorkStatus: request.WorkStatus,
}
if err := common.CreateStaffRecord(&staff); err != nil {
infra.Response.Error(ctx, err)
return
}
common.RespondCreatedResource(ctx, staff)
}
func UpdateStaff(ctx *gin.Context) {
point, _, ok := currentScope(ctx)
if !ok {
return
}
if _, ok := scopedStaff(ctx, ctx.Param("identity"), point); !ok {
return
}
var request staffRequest
if err := ctx.ShouldBindJSON(&request); err != nil ||
(request.WorkStatus != "on_duty" && request.WorkStatus != "off_duty") {
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
return
}
values := gin.H{"name": request.Name, "phone": request.Phone, "work_status": request.WorkStatus}
// 未选择新头像时不提交 avatar避免编辑基础资料误清空现有头像。
if request.Avatar != nil {
values["avatar"] = *request.Avatar
}
common.UpdateAllowedByIdentityWithError(ctx, &models.StaffAccount{}, values,
[]string{"name", "phone", "avatar", "work_status"}, common.StaffWriteError)
}
func ResetStaffPassword(ctx *gin.Context) {
point, _, ok := currentScope(ctx)
if !ok {
return
}
staff, ok := scopedStaff(ctx, ctx.Param("identity"), point)
if !ok {
return
}
var request struct {
Password string `json:"password" binding:"required"`
}
if err := ctx.ShouldBindJSON(&request); err != nil || !common.IsValidAccountPassword(request.Password) {
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
return
}
hash, _ := common.PasswordHash(request.Password)
if err := db().Model(&staff).Update("password_hash", hash).Error; err != nil {
infra.Response.Error(ctx, err)
return
}
infra.Response.Success(ctx, gin.H{"changed": true})
}
func UpdateStaffStatus(ctx *gin.Context) {
point, _, ok := currentScope(ctx)
if !ok {
return
}
if _, ok := scopedStaff(ctx, ctx.Param("identity"), point); ok {
common.UpdateRecordStatus(ctx, &models.StaffAccount{})
}
}
func ArchiveStaff(ctx *gin.Context) {
point, _, ok := currentScope(ctx)
if !ok {
return
}
staff, ok := scopedStaff(ctx, ctx.Param("identity"), point)
if !ok {
return
}
var count int64
if err := db().Model(&models.GasorderBasic{}).
Where("staff_account_id = ? AND order_status NOT IN ?", staff.ID, []int{common.StatusCompleted, common.StatusCancelled}).
Count(&count).Error; err != nil || count > 0 {
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
return
}
if err := db().Transaction(func(tx *gorm.DB) error {
if err := tx.Model(&models.StaffCredential{}).Where("staff_account_id = ?", staff.ID).
Update("status", common.StatusArchived).Error; err != nil {
return err
}
return tx.Model(&staff).Update("status", common.StatusArchived).Error
}); err != nil {
infra.Response.Error(ctx, err)
return
}
infra.Response.Success(ctx, gin.H{"archived": true})
}
func credentialQuery(point models.DeliveryBasic) *gorm.DB {
return common.ActiveRecords(db().Model(&models.StaffCredential{})).
Joins("JOIN staff_account ON staff_account.id = staff_credential.staff_account_id").
Where("staff_account.delivery_basic_id = ? AND staff_account.gas_basic_id = ? AND staff_account.role_code = ?",
point.ID, point.GasBasicID, "delivery")
}
func ListCredential(ctx *gin.Context) {
point, _, ok := currentScope(ctx)
if ok {
listScoped(ctx, &models.StaffCredential{}, credentialQuery(point), "staff_credential.created_at desc")
}
}
func GetCredential(ctx *gin.Context) {
point, _, ok := currentScope(ctx)
if !ok {
return
}
var credential models.StaffCredential
respondRecord(ctx, credentialQuery(point).Where("staff_credential.identity = ?", ctx.Param("identity")), &credential)
}
type credentialRequest struct {
StaffIdentity string `json:"staff_account_identity" binding:"required"`
CredentialType string `json:"credential_type" binding:"required,max=64"`
CredentialNo string `json:"credential_no" binding:"max=128"`
ExpiredAt *time.Time `json:"expired_at"`
}
func CreateCredential(ctx *gin.Context) {
point, _, ok := currentScope(ctx)
if !ok {
return
}
var request credentialRequest
if err := ctx.ShouldBindJSON(&request); err != nil {
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
return
}
staff, ok := scopedStaff(ctx, request.StaffIdentity, point)
if !ok {
return
}
item := models.StaffCredential{
Entity: common.NewEntity(common.StatusEnable), StaffAccountID: staff.ID,
CredentialType: request.CredentialType, CredentialNo: request.CredentialNo, ExpiredAt: request.ExpiredAt,
}
if err := db().Create(&item).Error; err != nil {
infra.Response.Error(ctx, err)
return
}
common.RespondCreatedResource(ctx, item)
}
func UpdateCredential(ctx *gin.Context) {
point, _, ok := currentScope(ctx)
if !ok {
return
}
var existing models.StaffCredential
if err := credentialQuery(point).Where("staff_credential.identity = ?", ctx.Param("identity")).First(&existing).Error; err != nil {
common.RespondRecordError(ctx, err)
return
}
var request credentialRequest
if err := ctx.ShouldBindJSON(&request); err != nil {
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
return
}
staff, ok := scopedStaff(ctx, request.StaffIdentity, point)
if !ok {
return
}
if err := db().Model(&existing).Updates(map[string]any{
"staff_account_id": staff.ID, "credential_type": request.CredentialType,
"credential_no": request.CredentialNo, "expired_at": request.ExpiredAt,
}).Error; err != nil {
infra.Response.Error(ctx, err)
return
}
infra.Response.Success(ctx, gin.H{"updated": true})
}
func UpdateCredentialStatus(ctx *gin.Context) {
point, _, ok := currentScope(ctx)
if !ok {
return
}
var count int64
if err := credentialQuery(point).Where("staff_credential.identity = ?", ctx.Param("identity")).Count(&count).Error; err != nil || count != 1 {
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
return
}
common.UpdateRecordStatus(ctx, &models.StaffCredential{})
}
func ArchiveCredential(ctx *gin.Context) {
point, _, ok := currentScope(ctx)
if !ok {
return
}
var count int64
if err := credentialQuery(point).Where("staff_credential.identity = ?", ctx.Param("identity")).Count(&count).Error; err != nil || count != 1 {
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
return
}
common.ArchiveRecord(ctx, &models.StaffCredential{})
}