Ver código fonte

feature:良渚需求优化

liuyaqi 2 anos atrás
pai
commit
1d8481c7c7

+ 26 - 0
common/date.go

@@ -2,6 +2,8 @@ package common
 
 import (
 	"fmt"
+	"time"
+
 	"github.com/gogf/gf/os/gtime"
 )
 
@@ -59,3 +61,27 @@ func GetNextTimeNode(signOutTime *gtime.Time) *gtime.Time {
 		signOutTime.Hour(), 30, 0)
 	return gtime.NewFromStr(nextTimeStr)
 }
+
+func SliceIntcontains(s []int, ele int) bool {
+	for _, i := range s {
+		if i == ele {
+			return true
+		}
+	}
+	return false
+}
+
+func Weekday(t time.Time) []time.Time {
+	year, month, day := t.Date()
+	var begin time.Time
+	if weekday := t.Weekday(); weekday == 0 {
+		begin = time.Date(year, month, day-6, 0, 0, 0, 0, t.Location())
+	} else {
+		begin = time.Date(year, month, day-int(weekday)+1, 0, 0, 0, 0, t.Location())
+	}
+	ret := []time.Time{}
+	for i := 0; i < 7; i++ {
+		ret = append(ret, begin.Add(time.Hour*24*time.Duration(i)))
+	}
+	return ret
+}

+ 3 - 0
config/config.toml

@@ -16,6 +16,9 @@
     end = "20:00:00"
     weekday = "1,2,3,4,5,6,7"
 
+[appointment]
+    time_split = 10 # 5、10、15、20、30、60 分钟
+
 [nsq]
     nsqLookupd = "192.168.0.252:4161"
     nsqd = "192.168.0.252:4150"

+ 6 - 0
dao/meeting/internal/meeting.go

@@ -38,6 +38,8 @@ type meetingColumns struct {
 	ImgUrl      string // 图片Url
 	Sort        string // 排序
 	Scale       string // 规模/可容纳人数
+	BeginAt     string
+	EndAt       string
 	Remark      string // 备注/说明
 	Location    string // 位置
 }
@@ -62,6 +64,8 @@ var (
 			ImgUrl:      "ImgUrl",
 			Sort:        "Sort",
 			Scale:       "Scale",
+			BeginAt:     "BeginAt",
+			EndAt:       "EndAt",
 			Remark:      "Remark",
 			Location:    "Location",
 		},
@@ -88,6 +92,8 @@ func NewMeetingDao(tenant string) MeetingDao {
 			ImgUrl:      "ImgUrl",
 			Sort:        "Sort",
 			Scale:       "Scale",
+			BeginAt:     "BeginAt",
+			EndAt:       "EndAt",
 			Remark:      "Remark",
 			Location:    "Location",
 		},

+ 37 - 0
handler/equipment.go

@@ -248,6 +248,43 @@ func (e *Equipment) AppointmentTime(ctx context.Context, req *equipment2.IdReq,
 	return nil
 }
 
+func (e *Equipment) AppointTimeSplit(ctx context.Context, req *struct{}, rsp *comm_def.CommonMsg) error {
+	tenant, err := micro_srv.GetTenant(ctx)
+	if err != nil {
+		return err
+	}
+	g.Log().Info("Received Equipment.AppointTimeSplit request @ " + tenant)
+	data := equipment.NewSrv(tenant).AppointTimeSplit()
+	if err != nil {
+		return err
+	}
+	rsp.Code = 200
+	rsp.Data = data
+	rsp.Msg = ""
+	return nil
+}
+
+func (e *Equipment) AppointTimeInfo(ctx context.Context, req *equipment2.AppointTimeInfoReq, rsp *comm_def.CommonMsg) error {
+	tenant, err := micro_srv.GetTenant(ctx)
+	if err != nil {
+		return err
+	}
+	info, err := micro_srv.GetUserInfo(ctx)
+	if err != nil {
+		return err
+	}
+	g.Log().Info("Received Equipment.AppointTimeInfo request @ " + tenant)
+	data, err := equipment.NewSrv(tenant).AppointTimeInfo(req, info)
+	_, err, code, msg := myerrors.CheckError(err, "查询预约信息错误")
+	if err != nil {
+		return err
+	}
+	rsp.Code = code
+	rsp.Data = data
+	rsp.Msg = msg
+	return nil
+}
+
 // SearchNoAppointment 查看不能预约时间段信息
 func (e *Equipment) SearchNoAppointment(ctx context.Context, req *equipment2.IdReq, rsp *comm_def.CommonMsg) error {
 	tenant, err := micro_srv.GetTenant(ctx)

+ 37 - 0
handler/reservation.go

@@ -119,6 +119,43 @@ func (r *Reservation) OverviewList(ctx context.Context, req *meeting.OverviewReq
 	return nil
 }
 
+func (e *Reservation) AppointTimeSplit(ctx context.Context, req *struct{}, rsp *comm_def.CommonMsg) error {
+	tenant, err := micro_srv.GetTenant(ctx)
+	if err != nil {
+		return err
+	}
+	g.Log().Info("Received Reservation.AppointTimeSplit request @ " + tenant)
+	data := reservation.NewSrv(tenant).AppointTimeSplit()
+	if err != nil {
+		return err
+	}
+	rsp.Code = 200
+	rsp.Data = data
+	rsp.Msg = ""
+	return nil
+}
+
+func (e *Reservation) AppointTimeInfo(ctx context.Context, req *meeting.AppointTimeInfoReq, rsp *comm_def.CommonMsg) error {
+	tenant, err := micro_srv.GetTenant(ctx)
+	if err != nil {
+		return err
+	}
+	info, err := micro_srv.GetUserInfo(ctx)
+	if err != nil {
+		return err
+	}
+	g.Log().Info("Received Reservation.AppointTimeInfo request @ " + tenant)
+	data, err := reservation.NewSrv(tenant).AppointTimeInfo(req, info)
+	_, err, code, msg := myerrors.CheckError(err, "查询会议室预约信息错误")
+	if err != nil {
+		return err
+	}
+	rsp.Code = code
+	rsp.Data = data
+	rsp.Msg = msg
+	return nil
+}
+
 // OverviewListByDay 预约概况
 func (r *Reservation) OverviewListByDay(ctx context.Context, req *meeting.OverviewReq, rsp *comm_def.CommonMsg) error {
 	tenant, err := micro_srv.GetTenant(ctx)

+ 7 - 0
model/equipment/base_equipment_qualification.go

@@ -6,6 +6,8 @@ package equipment
 
 import (
 	internal2 "lims_adapter/model/equipment/internal"
+
+	"github.com/gogf/gf/os/gtime"
 )
 
 // BaseEquipmentQualification is the golang structure for table base_equipment_qualification.
@@ -37,3 +39,8 @@ type IdReq struct {
 	Date        string `json:"date"`
 	Belongs     string `json:"belongs"`
 }
+
+type AppointTimeInfoReq struct {
+	InstrumentId int         `json:"instrument_id"` // 设备Id
+	Date         *gtime.Time `json:"date"`          // 返回这个日期所在星期的预约数据
+}

+ 17 - 15
model/meeting/internal/meeting.go

@@ -10,19 +10,21 @@ import (
 
 // Meeting is the golang structure for table meeting.
 type Meeting struct {
-	Id          int         `orm:"Id"  json:"id"`                    //
-	CreatedBy   string      `orm:"CreatedBy"   json:"created_by"`    // 创建人姓名
-	CreatedAt   *gtime.Time `orm:"CreatedAt"   json:"created_at"`    // 创建时间
-	UpdatedAt   *gtime.Time `orm:"UpdatedAt"   json:"updated_at"`    // 更新时间
-	UpdatedBy   string      `orm:"UpdatedBy"   json:"updated_by"`    // 更新人姓名
-	DeletedAt   *gtime.Time `orm:"DeletedAt"   json:"deleted_at"`    // 删除时间
-	UpdatedById int         `orm:"UpdatedById" json:"updated_by_id"` // 更新人ID
-	CreatedById int         `orm:"CreatedById" json:"created_by_id"` // 创建人ID
-	Code        string      `orm:"Code"        json:"code"`          // 会议室编码
-	Name        string      `orm:"Name"        json:"name"`          // 会议室名称
-	ImgUrl      string      `orm:"ImgUrl"      json:"img_url"`       // 图片Url
-	Sort        int         `orm:"Sort"        json:"sort"`          // 排序
-	Scale       int         `orm:"Scale"       json:"scale"`         // 规模/可容纳人数
-	Remark      string      `orm:"Remark"      json:"remark"`        // 备注/说明
-	Location    string      `orm:"Location"    json:"location"`      // 位置
+	Id          int         `orm:"Id"  json:"id"`                          //
+	CreatedBy   string      `orm:"CreatedBy"   json:"created_by"`          // 创建人姓名
+	CreatedAt   *gtime.Time `orm:"CreatedAt"   json:"created_at"`          // 创建时间
+	UpdatedAt   *gtime.Time `orm:"UpdatedAt"   json:"updated_at"`          // 更新时间
+	UpdatedBy   string      `orm:"UpdatedBy"   json:"updated_by"`          // 更新人姓名
+	DeletedAt   *gtime.Time `orm:"DeletedAt"   json:"deleted_at"`          // 删除时间
+	UpdatedById int         `orm:"UpdatedById" json:"updated_by_id"`       // 更新人ID
+	CreatedById int         `orm:"CreatedById" json:"created_by_id"`       // 创建人ID
+	Code        string      `orm:"Code"        json:"code"`                // 会议室编码
+	Name        string      `orm:"Name"        json:"name"`                // 会议室名称
+	ImgUrl      string      `orm:"ImgUrl"      json:"img_url"`             // 图片Url
+	Sort        int         `orm:"Sort"        json:"sort"`                // 排序
+	Scale       int         `orm:"Scale"       json:"scale"`               // 规模/可容纳人数
+	BeginAt     *gtime.Time `orm:"BeginAt"                 json:"beginAt"` // 预约开始时段
+	EndAt       *gtime.Time `orm:"EndAt"                   json:"endAt"`   // 预约结束时段
+	Remark      string      `orm:"Remark"      json:"remark"`              // 备注/说明
+	Location    string      `orm:"Location"    json:"location"`            // 位置
 }

+ 19 - 12
model/meeting/meeting.go

@@ -6,6 +6,8 @@ package meeting
 
 import (
 	internal2 "lims_adapter/model/meeting/internal"
+
+	"github.com/gogf/gf/os/gtime"
 )
 
 // Meeting is the golang structure for table meeting.
@@ -14,19 +16,24 @@ type Meeting internal2.Meeting
 // Fill with you ideas below.
 
 type MeetingReq struct {
-	Id       int    `orm:"Id,primary"  json:"id"`                            //
-	Code     string `orm:"Code"        json:"code"   v:"required#会议室编码不能为空"` // 会议室编码
-	Name     string `orm:"Name"        json:"name" v:"required#会议室名称不能为空"`   // 会议室名称
-	Scale    int    `orm:"Scale"       json:"scale"`                         // 规模/可容纳人数
-	Remark   string `orm:"Remark"      json:"remark"`                        // 备注/说明
-	Location string `orm:"Location"    json:"location"`                      // 位置
-	ImgUrl   string `orm:"ImgUrl"      json:"img_url"`                       // 图片Url
-	Sort     int    `orm:"Sort"        json:"sort"`                          // 排序
+	Id       int         `orm:"Id,primary"  json:"id"`                            //
+	Code     string      `orm:"Code"        json:"code"   v:"required#会议室编码不能为空"` // 会议室编码
+	Name     string      `orm:"Name"        json:"name" v:"required#会议室名称不能为空"`   // 会议室名称
+	Scale    int         `orm:"Scale"       json:"scale"`                         // 规模/可容纳人数
+	Remark   string      `orm:"Remark"      json:"remark"`                        // 备注/说明
+	Location string      `orm:"Location"    json:"location"`                      // 位置
+	ImgUrl   string      `orm:"ImgUrl"      json:"img_url"`                       // 图片Url
+	Sort     int         `orm:"Sort"        json:"sort"`                          // 排序
+	BeginAt  *gtime.Time `json:"beginAt"`                                         // 预约开始时段
+	EndAt    *gtime.Time `json:"endAt"`                                           // 预约结束时段
 }
 
 type ShortMeeting struct {
-	Id     int    `orm:"Id,primary"  json:"id"`      //
-	Code   string `orm:"Code"        json:"code"`    // 会议室编码
-	Name   string `orm:"Name"        json:"name"`    // 会议室名称
-	ImgUrl string `orm:"ImgUrl"      json:"img_url"` // 图片Url
+	Id      int         `orm:"Id,primary"  json:"id"`      //
+	Code    string      `orm:"Code"        json:"code"`    // 会议室编码
+	Name    string      `orm:"Name"        json:"name"`    // 会议室名称
+	ImgUrl  string      `orm:"ImgUrl"      json:"img_url"` // 图片Url
+	BeginAt *gtime.Time `json:"beginAt"`                   // 预约开始时段
+	EndAt   *gtime.Time `json:"endAt"`                     // 预约结束时段
+	Period  [][]string  `json:"period"`                    // 图片Url
 }

+ 17 - 10
model/meeting/meeting_reservation.go

@@ -67,6 +67,11 @@ type OverviewReq struct {
 	Date *gtime.Time `json:"date"`
 }
 
+type AppointTimeInfoReq struct {
+	MeetingId int         `json:"meeting_id"` // 会议室Id
+	Date      *gtime.Time `json:"date"`       // 返回这个日期所在星期的预约数据
+}
+
 type ShortList struct {
 	Id          int         `orm:"Id,primary"         json:"id"`            // 主键ID
 	Title       string      `orm:"Title"              json:"title"`         // 标题/会议名称
@@ -85,16 +90,18 @@ type ReserveReq struct {
 
 // ReservationList 预约详情
 type ReservationList struct {
-	EntityId      int    `json:"entity_id"`      // 会议室ID
-	Title         string `json:"title"`          // 会议名称
-	ReservationId int    `json:"reservation_id"` // 预约ID
-	Day           int    `json:"day"`            // 日期
-	Dept          string `json:"dept"`           // 部门
-	StartTime     string `json:"start_time"`     // 预约开始时间
-	Tel           string `json:"tel"`            // 手机号
-	Uid           int    `json:"uid"`            // 用户ID
-	Uname         string `json:"uname"`          // 用户名称
-	Week          int    `json:"week"`           // 本周时间
+	EntityId             int    `json:"entity_id"`              // 会议室ID
+	Title                string `json:"title"`                  // 会议名称
+	ReservationId        int    `json:"reservation_id"`         // 预约ID
+	Day                  int    `json:"day"`                    // 日期
+	Dept                 string `json:"dept"`                   // 部门
+	StartTime            string `json:"start_time"`             // 预约开始时间(格子表示的时间)
+	Tel                  string `json:"tel"`                    // 手机号
+	Uid                  int    `json:"uid"`                    // 用户ID
+	Uname                string `json:"uname"`                  // 用户名称
+	Week                 int    `json:"week"`                   // 本周时间
+	ReservationStartTime string `json:"reservation_start_time"` // 预约开始时间
+	ReservationEndTime   string `json:"reservation_end_time"`   // 预约结束时间
 }
 
 // EndingReq 预约请求

+ 206 - 5
service/equipment/equipment.go

@@ -1,19 +1,22 @@
 package equipment
 
 import (
-	"dashoo.cn/micro_libary/request"
 	"database/sql"
 	"errors"
 	"fmt"
-	"github.com/gogf/gf/frame/g"
-	"github.com/gogf/gf/os/gtime"
-	"github.com/gogf/gf/util/gconv"
+	"lims_adapter/common"
 	"lims_adapter/dao/equipment"
 	equipment2 "lims_adapter/model/equipment"
 	"lims_adapter/model/user"
 	"strconv"
 	"strings"
 	"time"
+
+	"dashoo.cn/micro_libary/myerrors"
+	"dashoo.cn/micro_libary/request"
+	"github.com/gogf/gf/frame/g"
+	"github.com/gogf/gf/os/gtime"
+	"github.com/gogf/gf/util/gconv"
 )
 
 // Service 设备
@@ -230,6 +233,204 @@ func (s Service) AppointmentTime(req *equipment2.IdReq) error {
 	return nil
 }
 
+func (s Service) AppointTimeSplit() int {
+	return g.Cfg().GetInt("appointment.time_split", 30)
+}
+
+type AppointTimeInfoAppoint struct {
+	Id     int    `json:"id"`
+	Start  string `json:"start"`
+	End    string `json:"end"`
+	Status int    `json:"status"`
+	Uid    int    `json:"uid"`
+	Uname  string `json:"uname"`
+	Tel    string `json:"tel"`
+	Dept   string `json:"dept"`
+}
+
+type AppointTimeInfoInstrument struct {
+	Id                 int
+	Weekday            string
+	BeginAt            *gtime.Time
+	EndAt              *gtime.Time
+	IsAppointAvailable int // 是否默认可预约 1是 2否
+
+}
+
+type TimeSpan struct {
+	Start *gtime.Time `json:"start"`
+	End   *gtime.Time `json:"end"`
+}
+
+// 获取指定仪器预约信息
+func (s Service) AppointTimeInfo(req *equipment2.AppointTimeInfoReq, userinfo request.UserInfo) (map[string]interface{}, error) {
+	if req.InstrumentId == 0 {
+		return nil, myerrors.NewMsgError(nil, "仪器 Id 不能为空")
+	}
+	if req.Date == nil {
+		return nil, myerrors.NewMsgError(nil, "日期不能为空")
+	}
+
+	timesplit := s.AppointTimeSplit()
+	days := common.Weekday(req.Date.Time)
+
+	var instr AppointTimeInfoInstrument
+	err := s.Dao.DB.Table("instrument").
+		Where("Id", req.InstrumentId).
+		Fields("Id, BeginAt, EndAt, Weekday, IsAppointAvailable").
+		Struct(&instr)
+	if err != nil {
+		return nil, err
+	}
+
+	disableExpired := g.Cfg().GetBool("appointment.disableExpired")
+	appointStatus := g.Slice{1, 2}
+	if disableExpired {
+		// 需要包含超时未上机的预约
+		appointStatus = g.Slice{1, 2, 10}
+	}
+
+	var appointInfo []AppointTimeInfoAppoint
+	err = s.Dao.DB.Table("appointment a").LeftJoin("base_user b", "a.UserId=b.Id").Unscoped().
+		Where("a.DeletedAt IS NULL").
+		Where("a.RelevanceId", req.InstrumentId).
+		WherePri("a.Status", appointStatus).
+		Where("a.EndTime > ?", days[0]).
+		Where("a.EndTime <= ?", days[6].Add(time.Hour*24)).
+		Fields("a.Id, a.StartTime as Start, a.EndTime as End, a.Status, a.UserId as Uid, b.Realname as Uname, b.Mobile as Tel, b.DepartmentName as Dept").
+		Structs(&appointInfo)
+	if err == sql.ErrNoRows {
+		err = nil
+	}
+	if err != nil {
+		return nil, err
+	}
+
+	instrWeekStr := strings.Split(instr.Weekday, ",")
+	instrWeek := []int{}
+	for _, i := range instrWeekStr {
+		d, err := strconv.Atoi(i)
+		if err != nil {
+			continue
+		}
+		instrWeek = append(instrWeek, d)
+	}
+
+	toRemoveDay := []int{}
+	for i := 1; i <= 7; i++ {
+		if !common.SliceIntcontains(instrWeek, i) {
+			toRemoveDay = append(toRemoveDay, i)
+		}
+	}
+
+	// 普通资格不可以预约周六周日的时间
+	if instr.IsAppointAvailable != 1 {
+		count, err := s.Dao.DB.Model("base_equipment_qualification").
+			Where("EquipmentId = ?", req.InstrumentId).
+			Where("UserId = ?", userinfo.Id).
+			Where("Qualification = 1").Count()
+		if err != nil {
+			return nil, err
+		}
+		if count != 0 {
+			if !common.SliceIntcontains(toRemoveDay, 6) {
+				toRemoveDay = append(toRemoveDay, 6)
+			}
+			if !common.SliceIntcontains(toRemoveDay, 7) {
+				toRemoveDay = append(toRemoveDay, 7)
+			}
+		}
+	}
+	// 优先预约权
+	advanceTimeList := []equipment2.BaseEquipmentAdvanceTime{}
+	qualificationCount, err := s.Dao.DB.Model("base_equipment_qualification").
+		Where("EquipmentId = ?", req.InstrumentId).
+		Where("UserId = ?", userinfo.Id).
+		Where("Qualification = 3").Count()
+	if err != nil {
+		return nil, err
+	}
+	if qualificationCount == 0 {
+		err := s.Dao.DB.Model("base_equipment_advance_time").
+			Where("EquipmentId = ?", req.InstrumentId).
+			Structs(&advanceTimeList)
+		if err == sql.ErrNoRows {
+			err = nil
+		}
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	unavailable := []TimeSpan{}
+	// 仪器设置的可预约范围外
+	for i, d := range days {
+		if common.SliceIntcontains(toRemoveDay, i+1) {
+			unavailable = append(unavailable, TimeSpan{
+				Start: gtime.NewFromTime(d),
+				End:   gtime.NewFromTime(d.Add(24 * time.Hour)),
+			})
+			continue
+		}
+		if instr.BeginAt.Hour() != 0 || instr.BeginAt.Minute() != 0 {
+			end := d.Add(
+				time.Hour*time.Duration(instr.BeginAt.Hour()) +
+					time.Minute*time.Duration(instr.BeginAt.Minute()))
+			unavailable = append(unavailable, TimeSpan{
+				Start: gtime.NewFromTime(d),
+				End:   gtime.NewFromTime(end),
+			})
+		}
+
+		endat := instr.EndAt.Add(time.Minute * time.Duration(timesplit))
+		if endat.Hour() != 0 || endat.Minute() != 0 {
+			start := d.Add(
+				time.Hour*time.Duration(endat.Hour()) +
+					time.Minute*time.Duration(endat.Minute()))
+			unavailable = append(unavailable, TimeSpan{
+				Start: gtime.NewFromTime(start),
+				End:   gtime.NewFromTime(d.Add(time.Hour * 24)),
+			})
+		}
+	}
+	// 优先预约时间段
+	for _, i := range advanceTimeList {
+		if i.Week == 0 {
+			continue
+		}
+		startTime, err := time.Parse("15:04", i.StartTime)
+		if err != nil {
+			continue
+		}
+		endTime, err := time.Parse("15:04", i.EndTime)
+		if err != nil {
+			continue
+		}
+		d := days[i.Week-1]
+
+		start := d.Add(
+			time.Hour*time.Duration(startTime.Hour()) +
+				time.Minute*time.Duration(startTime.Minute()))
+		end := d.Add(
+			time.Hour*time.Duration(endTime.Hour()) +
+				time.Minute*time.Duration(endTime.Minute()))
+
+		unavailable = append(unavailable, TimeSpan{
+			Start: gtime.NewFromTime(start),
+			End:   gtime.NewFromTime(end),
+		})
+	}
+
+	return map[string]interface{}{
+		"time_split":  timesplit,
+		"unavailable": unavailable,
+		"appoint":     appointInfo,
+	}, nil
+}
+
+// 默认可预约: 否表示只有加入预约资格设置可以预约,是表示预约资格设置失效,都可以预约
+// 普通资格不可以预约周六周日的时间,资深资格无限制
+// 优先预约权设置: 优先预约时段内只有添加到优先预约人员的用户才能预约
 // SearchNoAppointment 查看不能预约时间段信息
 func (s Service) SearchNoAppointment(req *equipment2.IdReq, info request.UserInfo) ([]equipment2.NoReservationInfo, error) {
 	// 普通资格限制周末时间
@@ -322,7 +523,7 @@ func noReservationInfo(startTime *gtime.Time, timeLong float64, Date string, wee
 	date, _ := gtime.StrToTime(Date, "Y-m-d")
 	// 获取当前星期对应的日期
 	day := GetDate(date, week)
-	grids := int(timeLong * 2)
+	grids := int(timeLong*2) + 1
 	for i := 0; i < grids; i++ {
 		// 开始时间
 		start_time := startTime.Add(time.Duration(i) * 30 * time.Minute)

+ 27 - 3
service/meeting/meeting.go

@@ -1,15 +1,17 @@
 package meeting
 
 import (
-	"dashoo.cn/micro_libary/request"
 	"errors"
-	"github.com/gogf/gf/frame/g"
-	"github.com/gogf/gf/util/gconv"
+	"fmt"
 	"lims_adapter/common"
 	"lims_adapter/dao/meeting"
 	"lims_adapter/model"
 	meeting2 "lims_adapter/model/meeting"
 	"lims_adapter/service"
+
+	"dashoo.cn/micro_libary/request"
+	"github.com/gogf/gf/frame/g"
+	"github.com/gogf/gf/util/gconv"
 )
 
 // Service 会议室服务
@@ -69,6 +71,17 @@ func (m Service) List(req model.ListReq) ([]meeting2.Meeting, int, error) {
 }
 
 func (m Service) Add(userInfo request.UserInfo, req meeting2.MeetingReq) error {
+	if !(req.BeginAt == nil && req.EndAt == nil) {
+		if req.BeginAt == nil {
+			return fmt.Errorf("预约开始时段不能为空")
+		}
+		if req.EndAt == nil {
+			return fmt.Errorf("预约结束时段不能为空")
+		}
+		if req.BeginAt.Sub(req.EndAt) >= 0 {
+			return fmt.Errorf("预约开始时段必须小于预约结束时段")
+		}
+	}
 	entity := new(meeting2.Meeting)
 	// 唯一校验
 	count, err := m.Dao.M.Fields(m.Dao.Columns.Id).Where(m.Dao.Columns.Name, req.Name).FindCount()
@@ -97,6 +110,17 @@ func (m Service) Add(userInfo request.UserInfo, req meeting2.MeetingReq) error {
 }
 
 func (m Service) Update(userInfo request.UserInfo, req meeting2.MeetingReq) error {
+	if !(req.BeginAt == nil && req.EndAt == nil) {
+		if req.BeginAt == nil {
+			return fmt.Errorf("预约开始时段不能为空")
+		}
+		if req.EndAt == nil {
+			return fmt.Errorf("预约结束时段不能为空")
+		}
+		if req.BeginAt.Sub(req.EndAt) >= 0 {
+			return fmt.Errorf("预约开始时段必须小于预约结束时段")
+		}
+	}
 	entity := new(meeting2.Meeting)
 	// 唯一校验
 	count, err := m.Dao.M.Fields(m.Dao.Columns.Id).

+ 174 - 18
service/reservation/reservation.go

@@ -2,14 +2,9 @@ package reservation
 
 import (
 	"context"
-	"dashoo.cn/common_definition/admin/user_def"
-	"dashoo.cn/micro_libary/micro_srv"
-	"dashoo.cn/micro_libary/request"
+	"database/sql"
 	"errors"
 	"fmt"
-	"github.com/gogf/gf/frame/g"
-	"github.com/gogf/gf/os/gtime"
-	"github.com/gogf/gf/util/gconv"
 	"lims_adapter/common"
 	meeting2 "lims_adapter/dao/meeting"
 	"lims_adapter/model"
@@ -17,7 +12,17 @@ import (
 	"lims_adapter/service"
 	"lims_adapter/service/meeting"
 	"lims_adapter/service/srv"
+	"strconv"
+	"strings"
 	"time"
+
+	"dashoo.cn/common_definition/admin/user_def"
+	"dashoo.cn/micro_libary/micro_srv"
+	"dashoo.cn/micro_libary/myerrors"
+	"dashoo.cn/micro_libary/request"
+	"github.com/gogf/gf/frame/g"
+	"github.com/gogf/gf/os/gtime"
+	"github.com/gogf/gf/util/gconv"
 )
 
 // Service ReservationService 预约
@@ -167,11 +172,147 @@ func (s Service) OverviewList(req meeting3.OverviewReq) (g.Map, error) {
 
 }
 
+func (s Service) AppointTimeSplit() int {
+	return g.Cfg().GetInt("reservation.time_split", 30)
+}
+
+type AppointTimeInfoAppoint struct {
+	Id     int    `json:"id"`
+	Title  string `json:"title"`
+	Start  string `json:"start"`
+	End    string `json:"end"`
+	Status int    `json:"status"`
+	Uid    int    `json:"uid"`
+	Uname  string `json:"uname"`
+	Tel    string `json:"tel"`
+	Dept   string `json:"dept"`
+}
+
+type AppointTimeInfoInstrument struct {
+	Id                 int
+	Weekday            string
+	BeginAt            *gtime.Time
+	EndAt              *gtime.Time
+	IsAppointAvailable int // 是否默认可预约 1是 2否
+
+}
+
+type TimeSpan struct {
+	Start *gtime.Time `json:"start"`
+	End   *gtime.Time `json:"end"`
+}
+
+// 获取指定仪器预约信息
+func (s Service) AppointTimeInfo(req *meeting3.AppointTimeInfoReq, userinfo request.UserInfo) (map[string]interface{}, error) {
+	if req.MeetingId == 0 {
+		return nil, myerrors.NewMsgError(nil, "会议室 Id 不能为空")
+	}
+	if req.Date == nil {
+		return nil, myerrors.NewMsgError(nil, "日期不能为空")
+	}
+
+	timesplit := s.AppointTimeSplit()
+	days := common.Weekday(req.Date.Time)
+
+	mettingSrv := meeting.NewSrv(s.Tenant)
+	m, err := mettingSrv.Dao.Where("Id = ?", req.MeetingId).One()
+	if err != nil {
+		return nil, err
+	}
+
+	var appointInfo []AppointTimeInfoAppoint
+	err = s.Dao.DB.Table("meeting_reservation a").LeftJoin("base_user b", "a.UserId=b.Id").Unscoped().
+		Where("a.DeletedAt IS NULL").
+		Where("a.EntityId", req.MeetingId).
+		WherePri("a.Status", 1).
+		Where("a.EndTime > ?", days[0]).
+		Where("a.EndTime <= ?", days[6].Add(time.Hour*24)).
+		Fields("a.Id, a.Title, a.StartTime as Start, a.EndTime as End, a.Status, a.UserId as Uid, b.Realname as Uname, b.Mobile as Tel, b.DepartmentName as Dept").
+		Structs(&appointInfo)
+	if err == sql.ErrNoRows {
+		err = nil
+	}
+	if err != nil {
+		return nil, err
+	}
+
+	reservationBeginStr := g.Cfg().GetString("reservation.begin", 30)
+	reservationEndStr := g.Cfg().GetString("reservation.end", 30)
+	reservationWeekdayStr := g.Cfg().GetString("reservation.weekday", 30)
+	instrWeekStr := strings.Split(reservationWeekdayStr, ",")
+	instrWeek := []int{}
+	for _, i := range instrWeekStr {
+		d, err := strconv.Atoi(i)
+		if err != nil {
+			continue
+		}
+		instrWeek = append(instrWeek, d)
+	}
+	toRemoveDay := []int{}
+	for i := 1; i <= 7; i++ {
+		if !common.SliceIntcontains(instrWeek, i) {
+			toRemoveDay = append(toRemoveDay, i)
+		}
+	}
+	reservationBegin, err := time.Parse("15:04:05", reservationBeginStr)
+	if err != nil {
+		return nil, err
+	}
+	reservationEnd, err := time.Parse("15:04:05", reservationEndStr)
+	if err != nil {
+		return nil, err
+	}
+	if m.BeginAt == nil {
+		m.BeginAt = gtime.NewFromTime(reservationBegin)
+	}
+	if m.EndAt == nil {
+		m.EndAt = gtime.NewFromTime(reservationEnd)
+	}
+
+	unavailable := []TimeSpan{}
+	// 仪器设置的可预约范围外
+	for i, d := range days {
+		if common.SliceIntcontains(toRemoveDay, i+1) {
+			unavailable = append(unavailable, TimeSpan{
+				Start: gtime.NewFromTime(d),
+				End:   gtime.NewFromTime(d.Add(24 * time.Hour)),
+			})
+			continue
+		}
+		if m.BeginAt.Hour() != 0 || m.BeginAt.Minute() != 0 {
+			end := d.Add(
+				time.Hour*time.Duration(m.BeginAt.Hour()) +
+					time.Minute*time.Duration(m.BeginAt.Minute()))
+			unavailable = append(unavailable, TimeSpan{
+				Start: gtime.NewFromTime(d),
+				End:   gtime.NewFromTime(end),
+			})
+		}
+
+		endat := m.EndAt.Add(time.Minute * time.Duration(timesplit))
+		if endat.Hour() != 0 || endat.Minute() != 0 {
+			start := d.Add(
+				time.Hour*time.Duration(endat.Hour()) +
+					time.Minute*time.Duration(endat.Minute()))
+			unavailable = append(unavailable, TimeSpan{
+				Start: gtime.NewFromTime(start),
+				End:   gtime.NewFromTime(d.Add(time.Hour * 24)),
+			})
+		}
+	}
+
+	return map[string]interface{}{
+		"time_split":  timesplit,
+		"unavailable": unavailable,
+		"appoint":     appointInfo,
+	}, nil
+}
+
 // OverviewListByDay 按天预约概况
 func (s Service) OverviewListByDay(ctx context.Context, req meeting3.OverviewReq) (g.Map, error) {
 	beginAt := g.Cfg().GetString("reservation.begin")
 	endAt := g.Cfg().GetString("reservation.end")
-	period := s.getCurrentPeriod(beginAt, endAt)
+	defaultPeriod := s.getCurrentPeriod(beginAt, endAt)
 	dates := getCurrentWeekDay(req.Date)
 	meetingList, err := meeting.NewSrv(s.Tenant).ShortList()
 	if err != nil {
@@ -181,8 +322,20 @@ func (s Service) OverviewListByDay(ctx context.Context, req meeting3.OverviewReq
 	if err != nil {
 		return nil, err
 	}
+
+	// for i := range meetingList {
+	// 	begin, end := beginAt, endAt
+	// 	if meetingList[i].BeginAt != nil && meetingList[i].EndAt != nil {
+	// 		begin = meetingList[i].BeginAt.Time.Format("15:04:05")
+	// 		end = meetingList[i].EndAt.Time.Format("15:04:05")
+	// 	}
+	// 	fmt.Println(meetingList[i].Id, meetingList[i].Name)
+	// 	period := s.getCurrentPeriod(begin, end)
+	// 	meetingList[i].Period = period
+	// }
+
 	return g.Map{
-		"period":      period,
+		"period":      defaultPeriod,
 		"dates":       dates,
 		"meeting":     meetingList,
 		"reservation": reservationList,
@@ -374,6 +527,7 @@ func (s Service) getCurrentPeriod(entityBeginAt, entityEndAt string) [][]string
 
 	timeLong := endAt.Sub(startAt).Hours()
 	grids := int(timeLong * 2)
+	fmt.Println(startAt, endAt)
 	var timeQuantum [][]string
 	for i := 0; i < grids; i++ {
 		times := make([]string, 0)
@@ -484,16 +638,18 @@ func (s Service) getCurrentDayReservation(ctx context.Context, meetingList []mee
 			for i := 0; i < int(grid); i++ {
 				startAt := v.StartTime.Add(time.Duration(i) * 30 * time.Minute)
 				resultList = append(resultList, meeting3.ReservationList{
-					EntityId:      value.Id,
-					ReservationId: int(v.Id),
-					Title:         v.Title,
-					Day:           v.StartTime.Day(),
-					Dept:          usersMap[v.UserId].DepartmentName,
-					StartTime:     fmt.Sprintf("%.2v:%.2v", startAt.Hour(), startAt.Minute()),
-					Tel:           usersMap[v.UserId].Mobile,
-					Uid:           v.UserId,
-					Uname:         usersMap[v.UserId].Realname,
-					Week:          common.GetCNWeekday(v.StartTime),
+					EntityId:             value.Id,
+					ReservationId:        int(v.Id),
+					Title:                v.Title,
+					Day:                  v.StartTime.Day(),
+					Dept:                 usersMap[v.UserId].DepartmentName,
+					StartTime:            fmt.Sprintf("%.2v:%.2v", startAt.Hour(), startAt.Minute()),
+					Tel:                  usersMap[v.UserId].Mobile,
+					Uid:                  v.UserId,
+					Uname:                usersMap[v.UserId].Realname,
+					Week:                 common.GetCNWeekday(v.StartTime),
+					ReservationStartTime: v.StartTime.String(),
+					ReservationEndTime:   v.EndTime.String(),
 				})
 			}
 		}

+ 3 - 0
sql/tmp.sql

@@ -0,0 +1,3 @@
+-- 2023-04-03
+alter table meeting add `BeginAt` datetime DEFAULT NULL COMMENT '预约开始时段' after Scale;
+alter table meeting add `EndAt` datetime DEFAULT NULL COMMENT '预约结束时段' after BeginAt;