package service import ( "errors" "fmt" "gorm.io/gorm" "hospital-admin-api/api/dao" "hospital-admin-api/api/dto" "hospital-admin-api/api/model" "hospital-admin-api/api/requests" "hospital-admin-api/global" "strconv" ) type PharmacyService struct{} // AddPharmacy 新增药房 func (r *PharmacyService) AddPharmacy(req requests.AddPharmacy) (bool, error) { pharmacyDao := dao.PharmacyDao{} // 检测药房代码是否已存在 existPharmacy, err := pharmacyDao.GetPharmacyByCode(req.PharmacyCode) if err == nil && existPharmacy != nil { return false, errors.New("药房代码已存在,请勿重复添加") } var province string var city string var county string areaDao := dao.AreaDao{} if req.ProvinceId != 0 { area, err := areaDao.GetAreaById(req.ProvinceId) if err != nil || area == nil { return false, errors.New("省份数据错误") } province = area.AreaName } if req.CityId != 0 { area, err := areaDao.GetAreaById(req.CityId) if err != nil || area == nil { return false, errors.New("城市数据错误") } city = area.AreaName } if req.CountyId != 0 { area, err := areaDao.GetAreaById(req.CountyId) if err != nil || area == nil { return false, errors.New("区县数据错误") } county = area.AreaName } status := 1 if req.Status != nil { status = *req.Status } // 开启事务 tx := global.Db.Begin() defer func() { if rec := recover(); rec != nil { tx.Rollback() } }() pharmacy := &model.Pharmacy{ PharmacyName: req.PharmacyName, PharmacyCode: req.PharmacyCode, Postage: *req.Postage, FreeShippingThreshold: *req.FreeShippingThreshold, IsPickup: *req.IsPickup, Telephone: req.Telephone, ProvinceId: req.ProvinceId, Province: province, CityId: req.CityId, City: city, CountyId: req.CountyId, County: county, Address: req.Address, Status: status, } pharmacy, err = pharmacyDao.AddPharmacy(tx, pharmacy) if err != nil || pharmacy == nil { tx.Rollback() return false, errors.New(err.Error()) } tx.Commit() return true, nil } // PutPharmacy 修改药房 func (r *PharmacyService) PutPharmacy(pharmacyId int64, req requests.PutPharmacy) (bool, error) { pharmacyDao := dao.PharmacyDao{} pharmacy, err := pharmacyDao.GetPharmacyById(pharmacyId) if err != nil || pharmacy == nil { return false, errors.New("药房不存在或已删除") } // 如果修改了代码,需检查新代码是否被其他药房占用 if req.PharmacyCode != pharmacy.PharmacyCode { exist, err := pharmacyDao.GetPharmacyByCode(req.PharmacyCode) if err == nil && exist != nil && exist.PharmacyId != pharmacyId { return false, errors.New("药房代码已被占用") } } // 若修改为禁用,校验是否有医生设为默认药房 if req.Status != nil && *req.Status == 0 && pharmacy.Status == 1 { doctorPharmacyDao := dao.DoctorPharmacyDao{} totalCount, defaultCount, err := doctorPharmacyDao.GetDoctorPharmacyCountsByPharmacyId(pharmacyId) if err != nil { return false, errors.New("查询药房关联医生失败") } if defaultCount > 0 { return false, fmt.Errorf("该药房已被 %d 位医生设为默认药房(共 %d 位医生绑定),无法禁用。请先在【药房关联医生】中调整默认药房或解除绑定后再试", defaultCount, totalCount) } } tx := global.Db.Begin() defer func() { if rec := recover(); rec != nil { tx.Rollback() fmt.Println(rec) } }() pharmacyData := make(map[string]interface{}) if req.PharmacyName != pharmacy.PharmacyName { pharmacyData["pharmacy_name"] = req.PharmacyName } if req.PharmacyCode != pharmacy.PharmacyCode { pharmacyData["pharmacy_code"] = req.PharmacyCode } if *req.Postage != pharmacy.Postage { pharmacyData["postage"] = *req.Postage } if *req.FreeShippingThreshold != pharmacy.FreeShippingThreshold { pharmacyData["free_shipping_threshold"] = *req.FreeShippingThreshold } if *req.IsPickup != pharmacy.IsPickup { pharmacyData["is_pickup"] = *req.IsPickup } if req.Telephone != pharmacy.Telephone { pharmacyData["telephone"] = req.Telephone } if req.ProvinceId != 0 && req.ProvinceId != pharmacy.ProvinceId { areaDao := dao.AreaDao{} area, err := areaDao.GetAreaById(req.ProvinceId) if err != nil || area == nil { return false, errors.New("省份数据错误") } pharmacyData["province_id"] = req.ProvinceId pharmacyData["province"] = area.AreaName } if req.CityId != 0 && req.CityId != pharmacy.CityId { areaDao := dao.AreaDao{} area, err := areaDao.GetAreaById(req.CityId) if err != nil || area == nil { return false, errors.New("城市数据错误") } pharmacyData["city_id"] = req.CityId pharmacyData["city"] = area.AreaName } if req.CountyId != 0 && req.CountyId != pharmacy.CountyId { areaDao := dao.AreaDao{} area, err := areaDao.GetAreaById(req.CountyId) if err != nil || area == nil { return false, errors.New("区县数据错误") } pharmacyData["county_id"] = req.CountyId pharmacyData["county"] = area.AreaName } if req.Address != pharmacy.Address { pharmacyData["address"] = req.Address } if req.Status != nil && *req.Status != pharmacy.Status { pharmacyData["status"] = *req.Status } if len(pharmacyData) > 0 { err = pharmacyDao.EditPharmacyById(tx, pharmacyId, pharmacyData) if err != nil { tx.Rollback() return false, errors.New(err.Error()) } } tx.Commit() return true, nil } // PutPharmacyStatus 修改药房状态 func (r *PharmacyService) PutPharmacyStatus(pharmacyId int64, req requests.PutPharmacyStatus) (bool, error) { pharmacyDao := dao.PharmacyDao{} pharmacy, err := pharmacyDao.GetPharmacyById(pharmacyId) if err != nil || pharmacy == nil { return false, errors.New("药房不存在或已删除") } // 若修改为禁用,校验是否有医生设为默认药房 if req.Status != nil && *req.Status == 0 { doctorPharmacyDao := dao.DoctorPharmacyDao{} totalCount, defaultCount, err := doctorPharmacyDao.GetDoctorPharmacyCountsByPharmacyId(pharmacyId) if err != nil { return false, errors.New("查询药房关联医生失败") } if defaultCount > 0 { return false, fmt.Errorf("该药房已被 %d 位医生设为默认药房(共 %d 位医生绑定),无法禁用。请先在【药房关联医生】中调整默认药房或解除绑定后再试", defaultCount, totalCount) } } data := map[string]interface{}{ "status": *req.Status, } tx := global.Db.Begin() defer func() { if rec := recover(); rec != nil { tx.Rollback() } }() err = pharmacyDao.EditPharmacyById(tx, pharmacyId, data) if err != nil { tx.Rollback() return false, errors.New(err.Error()) } tx.Commit() return true, nil } // DeletePharmacy 软删除药房 func (r *PharmacyService) DeletePharmacy(pharmacyId int64) (bool, error) { pharmacyDao := dao.PharmacyDao{} pharmacy, err := pharmacyDao.GetPharmacyById(pharmacyId) if err != nil || pharmacy == nil { return false, errors.New("药房不存在或已删除") } // 删除药房前校验:若有关联医生(特别是默认药房),拦截禁止删除 doctorPharmacyDao := dao.DoctorPharmacyDao{} totalCount, defaultCount, err := doctorPharmacyDao.GetDoctorPharmacyCountsByPharmacyId(pharmacyId) if err != nil { return false, errors.New("查询药房关联医生失败") } if defaultCount > 0 { return false, fmt.Errorf("该药房已被 %d 位医生设为默认药房(共 %d 位医生绑定),无法删除。请先在【药房关联医生】中调整默认药房或解除绑定后再试", defaultCount, totalCount) } if totalCount > 0 { return false, fmt.Errorf("该药房仍有 %d 位医生绑定,无法删除。请先在【药房关联医生】中解除绑定后再试", totalCount) } tx := global.Db.Begin() defer func() { if rec := recover(); rec != nil { tx.Rollback() } }() err = pharmacyDao.DeletePharmacyById(tx, pharmacyId) if err != nil { tx.Rollback() return false, errors.New(err.Error()) } tx.Commit() return true, nil } // GetPharmacy 药房详情 func (r *PharmacyService) GetPharmacy(pharmacyId int64) (*dto.PharmacyDto, error) { pharmacyDao := dao.PharmacyDao{} pharmacy, err := pharmacyDao.GetPharmacyById(pharmacyId) if err != nil || pharmacy == nil { if errors.Is(err, gorm.ErrRecordNotFound) || pharmacy == nil { return nil, errors.New("未找到相关药房信息") } return nil, err } return dto.GetPharmacyDto(pharmacy), nil } // GetPharmacyDoctorPage 获取药房绑定的医生列表-分页 func (r *PharmacyService) GetPharmacyDoctorPage(req requests.GetPharmacyDoctorPage) (map[string]interface{}, error) { pharmacyId, err := strconv.ParseInt(req.PharmacyId, 10, 64) if err != nil || pharmacyId < 0 { return nil, errors.New("药房id无效") } pharmacyDao := dao.PharmacyDao{} pharmacy, err := pharmacyDao.GetPharmacyById(pharmacyId) if err != nil || pharmacy == nil { return nil, errors.New("药房不存在或已被删除") } if req.Page <= 0 { req.Page = 1 } if req.PageSize <= 0 { req.PageSize = 20 } doctorPharmacyDao := dao.DoctorPharmacyDao{} list, total, err := doctorPharmacyDao.GetPharmacyDoctorPageSearch(pharmacyId, req, req.Page, req.PageSize) if err != nil { return nil, err } resList := dto.GetPharmacyDoctorListDto(list) result := make(map[string]interface{}) result["page"] = req.Page result["page_size"] = req.PageSize result["total"] = total result["data"] = resList return result, nil } // GetPharmacyDoctorList 获取药房绑定的全部医生列表 func (r *PharmacyService) GetPharmacyDoctorList(pharmacyId int64) ([]dto.PharmacyDoctorDto, error) { pharmacyDao := dao.PharmacyDao{} pharmacy, err := pharmacyDao.GetPharmacyById(pharmacyId) if err != nil || pharmacy == nil { return nil, errors.New("药房不存在或已被删除") } doctorPharmacyDao := dao.DoctorPharmacyDao{} list, err := doctorPharmacyDao.GetPharmacyDoctorListByPharmacyId(pharmacyId) if err != nil { return nil, err } return dto.GetPharmacyDoctorListDto(list), nil } // GetAffectedDoctors 获取药房下线受影响医生列表(预检接口) func (r *PharmacyService) GetAffectedDoctors(pharmacyId int64) (*dto.PharmacyAffectedDoctorsDto, error) { pharmacyDao := dao.PharmacyDao{} pharmacy, err := pharmacyDao.GetPharmacyById(pharmacyId) if err != nil || pharmacy == nil { return nil, errors.New("药房不存在或已删除") } doctorPharmacyDao := dao.DoctorPharmacyDao{} totalCount, defaultCount, err := doctorPharmacyDao.GetDoctorPharmacyCountsByPharmacyId(pharmacyId) if err != nil { return nil, errors.New("查询药房关联医生数量失败") } // 获取将当前药房设为默认药房的受影响医生列表(仅默认药房受影响的医生需要重新指定默认药房) list, err := doctorPharmacyDao.GetDefaultPharmacyDoctorListByPharmacyId(pharmacyId) if err != nil { return nil, errors.New("查询受影响医生列表失败") } // 收集所有医生ID doctorIds := make([]int64, 0, len(list)) for _, dp := range list { doctorIds = append(doctorIds, dp.DoctorId) } // 批量查询这些医生绑定的其他有效药房 otherMap := make(map[int64][]dto.BoundPharmacySimpleDto) if len(doctorIds) > 0 { otherList, err := doctorPharmacyDao.GetOtherPharmaciesByDoctorIds(doctorIds, pharmacyId) if err == nil { for _, dp := range otherList { if dp.Pharmacy != nil && dp.Pharmacy.Status == 1 { otherMap[dp.DoctorId] = append(otherMap[dp.DoctorId], dto.BoundPharmacySimpleDto{ PharmacyId: strconv.FormatInt(dp.Pharmacy.PharmacyId, 10), PharmacyName: dp.Pharmacy.PharmacyName, PharmacyCode: dp.Pharmacy.PharmacyCode, IsDefault: dp.IsDefault, }) } } } } doctorItems := make([]dto.AffectedDoctorItemDto, 0, len(list)) for _, dp := range list { item := dto.AffectedDoctorItemDto{ DoctorId: strconv.FormatInt(dp.DoctorId, 10), DoctorPharmacyId: strconv.FormatInt(dp.DoctorPharmacyId, 10), IsDefault: dp.IsDefault, } if dp.UserDoctor != nil { item.UserId = strconv.FormatInt(dp.UserDoctor.UserId, 10) item.UserName = dp.UserDoctor.UserName item.DoctorTitleName = dto.GetDoctorTitleName(dp.UserDoctor.DoctorTitle) item.HospitalId = strconv.FormatInt(dp.UserDoctor.HospitalID, 10) if dp.UserDoctor.Hospital != nil { item.HospitalName = dp.UserDoctor.Hospital.HospitalName } item.DepartmentCustomId = strconv.FormatInt(dp.UserDoctor.DepartmentCustomId, 10) item.DepartmentCustomName = dp.UserDoctor.DepartmentCustomName if dp.UserDoctor.User != nil { item.Mobile = dp.UserDoctor.User.Mobile } } if others, ok := otherMap[dp.DoctorId]; ok { item.OtherBoundPharmacies = others } else { item.OtherBoundPharmacies = []dto.BoundPharmacySimpleDto{} } doctorItems = append(doctorItems, item) } res := &dto.PharmacyAffectedDoctorsDto{ PharmacyId: strconv.FormatInt(pharmacy.PharmacyId, 10), PharmacyName: pharmacy.PharmacyName, PharmacyCode: pharmacy.PharmacyCode, Status: pharmacy.Status, DefaultDoctorCount: defaultCount, TotalDoctorCount: totalCount, DoctorList: doctorItems, } return res, nil } // BatchTransferAndAction 批量迁移医生默认药房并执行药房操作(禁用/删除) func (r *PharmacyService) BatchTransferAndAction(req requests.BatchTransferAndAction) (bool, error) { sourcePharmacyId, err := strconv.ParseInt(req.SourcePharmacyId, 10, 64) if err != nil || sourcePharmacyId < 0 { return false, errors.New("原药房id无效") } if req.Action != "disable" && req.Action != "delete" { return false, errors.New("操作类型仅支持 disable(禁用) 或 delete(删除)") } pharmacyDao := dao.PharmacyDao{} sourcePharmacy, err := pharmacyDao.GetPharmacyById(sourcePharmacyId) if err != nil || sourcePharmacy == nil { return false, errors.New("原药房不存在或已删除") } // 查询当前以原药房为默认药房的所有医生 doctorPharmacyDao := dao.DoctorPharmacyDao{} var defaultDoctorPharmacies []*model.DoctorPharmacy err = global.Db.Where("pharmacy_id = ? AND status = 1 AND is_default = 1", sourcePharmacyId).Find(&defaultDoctorPharmacies).Error if err != nil { return false, errors.New("查询受影响医生失败") } defaultDoctorMap := make(map[int64]bool) for _, dp := range defaultDoctorPharmacies { defaultDoctorMap[dp.DoctorId] = true } // 校验 transfers transferDoctorMap := make(map[int64]int64) // doctorId -> targetPharmacyId targetPharmacyIdSet := make(map[int64]bool) for _, item := range req.Transfers { docId, err := strconv.ParseInt(item.DoctorId, 10, 64) if err != nil || docId <= 0 { return false, errors.New("迁移配置中包含无效的医生id: " + item.DoctorId) } targetPharId, err := strconv.ParseInt(item.TargetPharmacyId, 10, 64) if err != nil || targetPharId < 0 { return false, errors.New("迁移配置中包含无效的目标药房id: " + item.TargetPharmacyId) } if targetPharId == sourcePharmacyId { return false, errors.New("目标药房不能是当前待下线的药房自身") } if _, exists := transferDoctorMap[docId]; exists { return false, fmt.Errorf("医生(ID: %d)存在重复迁移配置,每位医生只能指定一个默认药房", docId) } transferDoctorMap[docId] = targetPharId targetPharmacyIdSet[targetPharId] = true } // 强校验:原药房名下所有默认医生必须全部指定了替代药房 for docId := range defaultDoctorMap { if _, ok := transferDoctorMap[docId]; !ok { return false, fmt.Errorf("医生(ID: %d)当前以此药房为默认药房,必须为其指定替代药房", docId) } } // 校验所有目标药房必须存在且处于正常状态 (status = 1) if len(targetPharmacyIdSet) > 0 { var targetPharIds []int64 for id := range targetPharmacyIdSet { targetPharIds = append(targetPharIds, id) } var targetPharmacies []*model.Pharmacy err = global.Db.Where("pharmacy_id IN (?)", targetPharIds).Find(&targetPharmacies).Error if err != nil { return false, errors.New("查询目标药房信息失败") } targetMap := make(map[int64]*model.Pharmacy) for _, ph := range targetPharmacies { targetMap[ph.PharmacyId] = ph } for _, targetId := range targetPharIds { ph, exists := targetMap[targetId] if !exists || ph == nil { return false, fmt.Errorf("目标药房(ID: %d)不存在或已被删除", targetId) } if ph.Status != 1 { return false, fmt.Errorf("目标药房【%s】处于禁用状态,无法作为替代药房", ph.PharmacyName) } } } // 开启事务执行迁移与药房操作 tx := global.Db.Begin() defer func() { if rec := recover(); rec != nil { tx.Rollback() } }() // 逐个医生处理目标药房绑定与默认设置 for docId, targetPharId := range transferDoctorMap { // 1. 重置该医生名下现有的所有药房为非默认 if err := doctorPharmacyDao.ResetDoctorDefaultPharmacy(tx, docId); err != nil { tx.Rollback() return false, errors.New("重置医生默认药房失败") } // 2. 检查该医生是否已绑定目标药房 existTarget, _ := doctorPharmacyDao.GetDoctorPharmacyByDoctorAndPharmacy(docId, targetPharId) if existTarget != nil { // 更新为 status = 1, is_default = 1 err = tx.Model(&model.DoctorPharmacy{}). Where("doctor_pharmacy_id = ?", existTarget.DoctorPharmacyId). Updates(map[string]interface{}{ "is_default": 1, "status": 1, }).Error if err != nil { tx.Rollback() return false, errors.New("更新目标药房绑定关系失败") } } else { // 新增绑定记录 newBinding := &model.DoctorPharmacy{ DoctorId: docId, PharmacyId: targetPharId, IsDefault: 1, Status: 1, } _, err = doctorPharmacyDao.AddDoctorPharmacy(tx, newBinding) if err != nil { tx.Rollback() return false, errors.New("新增目标药房绑定关系失败: " + err.Error()) } } // 3. 处理医生与原药房关系 if req.Action == "delete" || (req.UnbindSource != nil && *req.UnbindSource == 1) { _ = doctorPharmacyDao.DeleteDoctorPharmacyByDoctorAndPharmacy(tx, docId, sourcePharmacyId) } } // 执行原药房下线操作 if req.Action == "disable" { // 若选择了解绑原药房,清空原药房所有绑定记录 if req.UnbindSource != nil && *req.UnbindSource == 1 { _ = doctorPharmacyDao.DeleteDoctorPharmacy(tx, map[string]interface{}{"pharmacy_id": sourcePharmacyId}) } data := map[string]interface{}{ "status": 0, } err = pharmacyDao.EditPharmacyById(tx, sourcePharmacyId, data) if err != nil { tx.Rollback() return false, errors.New("修改药房状态失败: " + err.Error()) } } else if req.Action == "delete" { // 删除药房必须清空所有关联记录,防止外键孤儿数据 _ = doctorPharmacyDao.DeleteDoctorPharmacy(tx, map[string]interface{}{"pharmacy_id": sourcePharmacyId}) err = pharmacyDao.DeletePharmacyById(tx, sourcePharmacyId) if err != nil { tx.Rollback() return false, errors.New("删除药房失败: " + err.Error()) } } tx.Commit() return true, nil }