فهرست منبع

fix:象屿研究员考核接口

Roc 1 هفته پیش
والد
کامیت
98941c71a9

+ 147 - 0
controllers/xy/base_auth_xy.go

@@ -0,0 +1,147 @@
+package xy
+
+import (
+	"encoding/json"
+	"eta/eta_hub/models/system"
+	"eta/eta_hub/models/xy"
+	"eta/eta_hub/utils"
+	"fmt"
+	"github.com/beego/beego/v2/server/web"
+	"net/http"
+	"net/url"
+)
+
+type BaseAuthXyController struct {
+	web.Controller
+	SysUser *system.Admin
+}
+
+func (this *BaseAuthXyController) Prepare() {
+	fmt.Println("enter prepare")
+	method := this.Ctx.Input.Method()
+	uri := this.Ctx.Input.URI()
+	fmt.Println("Url:", uri)
+	if method != "HEAD" {
+		//校验签名
+		nonce := this.Ctx.Input.Header("nonce")
+		timestamp := this.Ctx.Input.Header("timestamp")
+		appid := this.Ctx.Input.Header("appid")
+		signature := this.Ctx.Input.Header("signature")
+
+		if nonce == "" {
+			errMsg := "随机字符串不能为空"
+			this.JSON(xy.BaseResponse{ReturnCode: "E", Status: "E", Msg: "", ErrMsg: errMsg}, false, false)
+			this.StopRun()
+			return
+		}
+
+		if timestamp == "" {
+			errMsg := "时间戳不能为空"
+			this.JSON(xy.BaseResponse{ReturnCode: "E", Status: "E", Msg: "", ErrMsg: errMsg}, false, false)
+			this.StopRun()
+			return
+		}
+
+		if appid != utils.AppId {
+			errMsg := "商家AppId错误,请核查"
+			this.JSON(xy.BaseResponse{ReturnCode: "E", Status: "E", Msg: "", ErrMsg: errMsg}, false, false)
+			this.StopRun()
+			return
+		}
+
+		checkSign := utils.GetSign(nonce, timestamp)
+		if signature != checkSign {
+			fmt.Printf("用户提交签名:%s;\n系统生成签名:%s\n", signature, checkSign)
+			errMsg := "签名错误"
+			this.JSON(xy.BaseResponse{ReturnCode: "E", Status: "E", Msg: "", ErrMsg: errMsg}, false, false)
+			this.StopRun()
+			return
+		}
+		if method != "GET" && method != "POST" {
+			errMsg := "无效的请求方式"
+			this.JSON(xy.BaseResponse{ReturnCode: "E", Status: "E", Msg: "", ErrMsg: errMsg}, false, false)
+			this.StopRun()
+			return
+		}
+	} else {
+		this.JSON(xy.BaseResponse{ReturnCode: "E", Status: "E", Msg: "系统异常,请联系客服!", ErrMsg: "method:" + method}, false, false)
+		this.StopRun()
+		return
+	}
+}
+
+func (c *BaseAuthXyController) ServeJSON(encoding ...bool) {
+	var (
+		hasIndent   = false
+		hasEncoding = false
+	)
+	if web.BConfig.RunMode == web.PROD {
+		hasIndent = false
+	}
+	if len(encoding) > 0 && encoding[0] == true {
+		hasEncoding = true
+	}
+	if c.Data["json"] == nil {
+		//go utils.SendEmail("异常提醒:", "接口:"+"URI:"+c.Ctx.Input.URI()+";无返回值", utils.EmailSendToUsers)
+		//body := "接口:" + "URI:" + c.Ctx.Input.URI() + ";无返回值"
+		//go alarm_msg.SendAlarmMsg(body, 1)
+		return
+	}
+
+	//baseRes := c.Data["json"].(*xy.BaseResponse)
+	//if baseRes != nil {
+	//	body, _ := json.Marshal(baseRes)
+	//	var requestBody string
+	//	method := c.Ctx.Input.Method()
+	//	if method == "GET" {
+	//		requestBody = c.Ctx.Request.RequestURI
+	//	} else {
+	//		requestBody, _ = url.QueryUnescape(string(c.Ctx.Input.RequestBody))
+	//	}
+	//	//记录错误日志, 并清掉错误信息避免暴露给外部
+	//	if baseRes.ErrMsg != "" {
+	//		utils.FileLog.Info(baseRes.ErrMsg)
+	//		baseRes.ErrMsg = ""
+	//	}
+	//}
+	c.JSON(c.Data["json"], hasIndent, hasEncoding)
+}
+
+func (c *BaseAuthXyController) JSON(data interface{}, hasIndent bool, coding bool) error {
+	c.Ctx.Output.Header("Content-Type", "application/json; charset=utf-8")
+	var content []byte
+	var err error
+	if hasIndent {
+		content, err = json.MarshalIndent(data, "", "  ")
+	} else {
+		content, err = json.Marshal(data)
+	}
+	if err != nil {
+		http.Error(c.Ctx.Output.Context.ResponseWriter, err.Error(), http.StatusInternalServerError)
+		return err
+	}
+	ip := c.Ctx.Input.IP()
+	requestBody, err := url.QueryUnescape(string(c.Ctx.Input.RequestBody))
+	if err != nil {
+		requestBody = string(c.Ctx.Input.RequestBody)
+	}
+	if requestBody == "" {
+		requestBody = c.Ctx.Input.URI()
+	}
+	c.logUri(content, requestBody, ip)
+	if coding {
+		content = []byte(utils.StringsToJSON(string(content)))
+	}
+	return c.Ctx.Output.Body(content)
+}
+
+func (c *BaseAuthXyController) logUri(respContent []byte, requestBody, ip string) {
+	var reqData interface{}
+	err := json.Unmarshal([]byte(requestBody), &reqData)
+	if err != nil {
+		utils.ApiLog.Info("uri:%s, requestBody:%s, ip:%s", c.Ctx.Input.URI(), requestBody, ip)
+	} else {
+		utils.ApiLog.Info("uri:%s, requestBody:%s, ip:%s", c.Ctx.Input.URI(), requestBody, ip)
+	}
+	return
+}

+ 277 - 0
controllers/xy/researcher_assessment.go

@@ -0,0 +1,277 @@
+package xy
+
+import (
+	"encoding/json"
+	"eta/eta_hub/models"
+	"eta/eta_hub/models/system"
+	"eta/eta_hub/models/xy"
+	"eta/eta_hub/utils"
+	"fmt"
+	"strconv"
+	"time"
+)
+
+// AssessmentFormController 研究员填报
+type AssessmentFormController struct {
+	BaseAuthXyController
+}
+
+// Check
+// @Title 校验用户是否需要填报
+// @Description 校验用户是否需要填报
+// @Success 200 {object} xy.AssessmentCheckResp
+// @router /user/check [post]
+func (c *AssessmentFormController) Check() {
+	br := new(xy.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+
+	var req xy.AssessmentCheckReq
+	if e := json.Unmarshal(c.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("参数解析失败, %v", e)
+		return
+	}
+
+	var resp xy.AssessmentCheckResp
+	resp.SubmitAuth = true
+	br.Data = resp
+
+	// 找到系统账户
+	adminIfo, err := system.GetUserByOutId(req.Loginid)
+	if err != nil {
+		msg := `获取失败`
+		errMsg := "获取失败,Err:" + err.Error()
+		if utils.IsErrNoRow(err) {
+			msg = `找不到系统账户`
+			errMsg = msg
+		}
+		br.Msg = msg
+		br.ErrMsg = errMsg
+		return
+	}
+
+	// 根据账号获取是否配置研究员信息
+	researcherObj := new(models.AssessmentResearcher)
+	cond := fmt.Sprintf(` AND %s = ? AND %s = 1 `, researcherObj.Cols().AdminId, researcherObj.Cols().Enabled)
+	pars := make([]interface{}, 0)
+	pars = append(pars, adminIfo.AdminId)
+	researcherItem, err := researcherObj.GetItemByCondition(cond, pars, "")
+	if err != nil {
+		if !utils.IsErrNoRow(err) {
+			br.Msg = `获取失败`
+			br.ErrMsg = "获取失败,Err:" + err.Error()
+			return
+		}
+		br.ReturnCode = "S"
+		br.Status = "S"
+		br.Msg = "获取成功"
+		return
+	}
+	// 根据研究员ID获取配置的品种数量
+	varietyMappingObj := new(models.AssessmentResearcherVarietyMapping)
+	cond = fmt.Sprintf(` AND %s = ?  `, varietyMappingObj.Cols().AssessmentResearcherId)
+	pars = make([]interface{}, 0)
+	pars = append(pars, researcherItem.AssessmentResearcherId)
+	count, err := varietyMappingObj.GetCountByCondition(cond, pars)
+	if err != nil {
+		br.Msg = `获取失败`
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+
+	if count > 0 {
+		resp.DataAuth = true
+	}
+
+	br.Data = resp
+	br.ReturnCode = "S"
+	br.Status = "S"
+	br.Msg = "获取成功"
+}
+
+// Detail
+// @Title 填报单详情
+// @Description 填报单详情
+// @Success 200 string "获取成功"
+// @router /forms/detail [post]
+func (c *AssessmentFormController) Detail() {
+	br := new(xy.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+
+	var req xy.AssessmentFormQueryReq
+	if e := json.Unmarshal(c.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("参数解析失败, %v", e)
+		return
+	}
+
+	if req.Loginid == "" {
+		br.Msg = "参数有误"
+		return
+	}
+
+	// 找到系统账户
+	adminIfo, err := system.GetUserByOutId(req.Loginid)
+	if err != nil {
+		msg := `获取失败`
+		errMsg := "获取失败,Err:" + err.Error()
+		if utils.IsErrNoRow(err) {
+			msg = `找不到系统账户`
+			errMsg = msg
+		}
+		br.Msg = msg
+		br.ErrMsg = errMsg
+		return
+	}
+
+	var startTime, endTime time.Time
+
+	if req.StartTime == "" {
+		br.Msg = "开始日期为空"
+		return
+	} else {
+		startTime, err = time.ParseInLocation(utils.FormatDate, req.StartTime, time.Local)
+		if err != nil {
+			br.Msg = "开始时间异常"
+			br.Msg = "开始时间异常,Err:" + err.Error()
+			return
+		}
+	}
+	if req.EndTime == "" {
+		endTime = time.Now()
+	}
+
+	startWeekTime, _, _ := utils.GetAssessmentWeekAndFriday(startTime)
+	endWeekTime, _, _ := utils.GetAssessmentWeekAndFriday(endTime)
+	if startWeekTime != endWeekTime {
+		br.Msg = `开始日期和结束日期不在同一周`
+		br.ErrMsg = `开始日期和结束日期不在同一周`
+		return
+	}
+
+	resp := new(models.AssessmentFormViewResp)
+
+	// 根据单号获取填报单
+	formOb := new(models.AssessmentForm)
+	cond := fmt.Sprintf(` AND %s = ? AND %s = ? `, formOb.Cols().ResearcherAdminId, formOb.Cols().WeekTime)
+	pars := make([]interface{}, 0)
+	pars = append(pars, adminIfo.AdminId, startWeekTime)
+	list, e := formOb.GetItemsByCondition(cond, pars, []string{}, "")
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("获取本周填报单总数失败, %v", e)
+		return
+	}
+	if len(list) > 0 {
+		resp.DataId = list[0].FormCode
+		resp.OutNo = list[0].OutNo
+		resp.Status = list[0].Status
+		resp.OutStatus = list[0].OutStatus
+	}
+	resp.Head = models.AssessmentFormDetail{
+		ResearcherName:       "研究员名称",
+		VarietyCode:          "品种编码",
+		VarietyName:          "品种名称",
+		WeekTime:             "周度",
+		BaseDate:             "价格基准日期",
+		MonthlyPriceForecast: "月度涨跌",
+		WeeklyUpForecast:     "周度上行风险",
+		WeeklyDownForecast:   "周度下行风险",
+		SubmitTime:           "提交时间",
+		CreateTime:           "创建时间",
+		ModifyTime:           "修改时间",
+	}
+	resp.List = make([]*models.AssessmentFormDetail, 0)
+	for _, v := range list {
+		resp.List = append(resp.List, v.Format2Detail())
+	}
+
+	br.Data = resp
+	br.ReturnCode = "S"
+	br.Status = "S"
+	//br.Success = true
+	br.Msg = "获取成功"
+}
+
+// UpdateOutStatus
+// @Title 更新填报单
+// @Description 更新填报单
+// @Success 200 string "获取成功"
+// @router /forms/update [post]
+func (c *AssessmentFormController) UpdateOutStatus() {
+	br := new(xy.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	br.Data = ``
+
+	var req xy.UpdateAssessmentFormReq
+	if e := json.Unmarshal(c.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("参数解析失败, %v", e)
+		return
+	}
+
+	if req.DataId == "" {
+		br.Msg = "数据ID异常"
+		return
+	}
+	if req.ReportId == "" {
+		br.Msg = "周报ID异常"
+		return
+	}
+	if req.Status == "" {
+		br.Msg = "status异常"
+		return
+	}
+	status, err := strconv.Atoi(req.Status)
+	if err != nil {
+		br.Msg = "status异常"
+		br.ErrMsg = "status异常,err:" + err.Error()
+		return
+	}
+	if !utils.IsCheckInList([]int{0, 1}, status) {
+		br.Msg = "status异常"
+		return
+	}
+
+	// 根据单号获取填报单
+	formOb := new(models.AssessmentForm)
+	cond := fmt.Sprintf(` AND %s = ?`, formOb.Cols().FormCode)
+	pars := make([]interface{}, 0)
+	pars = append(pars, req.DataId)
+	list, e := formOb.GetItemsByCondition(cond, pars, []string{}, "")
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("获取本周填报单总数失败, %v", e)
+		return
+	}
+	if len(list) <= 0 {
+		br.Msg = "数据不存在"
+		return
+	}
+	if list[0].Status != 1 {
+		br.Msg = "填报单未提交"
+		return
+	}
+
+	err = formOb.UpdateOutStatusByFormCode(req.DataId, req.ReportId, status)
+	if err != nil {
+		br.Msg = "更新失败"
+		br.ErrMsg = fmt.Sprintf("更新失败, %v", err)
+		return
+	}
+
+	br.Data = ``
+	br.ReturnCode = "S"
+	br.Status = "S"
+	//br.Success = true
+	br.Msg = "获取成功"
+}

+ 246 - 0
models/assessment_form.go

@@ -0,0 +1,246 @@
+package models
+
+import (
+	"eta/eta_hub/global"
+	"eta/eta_hub/utils"
+	"fmt"
+	"strings"
+	"time"
+)
+
+type AssessmentForm struct {
+	AssessmentFormId     int       `gorm:"column:assessment_form_id;primaryKey;autoIncrement"`
+	FormCode             string    `description:"单号"`
+	ResearcherId         int       `description:"研究员ID"`
+	ResearcherAdminId    int       `description:"研究员用户ID"`
+	ResearcherName       string    `description:"研究员姓名"`
+	VarietyId            int       `description:"品种ID"`
+	VarietyCode          string    `description:"品种编码"`
+	VarietyName          string    `description:"品种名称"`
+	WeekTime             string    `description:"周度(格式:202501,202502)"`
+	WeekStart            time.Time `description:"当周开始日期"`
+	WeekEnd              time.Time `description:"当周结束日期"`
+	BaseDate             time.Time `description:"价格基准日期"`
+	MonthlyPriceForecast string    `description:"月度涨跌:涨/跌/震荡"`
+	WeeklyUpForecast     string    `description:"周度上行风险:是/否"`
+	WeeklyDownForecast   string    `description:"周度下行风险:是/否"`
+	Status               int       `description:"状态:0-草稿;1-已提交;"`
+	OutNo                string    `description:"外部编码(客户内部系统编码)"`
+	OutStatus            int       `description:"外部状态(象屿):0-未使用;1-已使用"`
+	SubmitTime           time.Time `description:"提交时间"`
+	CreateTime           time.Time `description:"创建时间"`
+	ModifyTime           time.Time `description:"修改时间"`
+}
+
+func (m *AssessmentForm) TableName() string {
+	return "assessment_form"
+}
+
+type AssessmentFormCols struct {
+	PrimaryId            string
+	FormCode             string
+	ResearcherId         string
+	ResearcherAdminId    string
+	ResearcherName       string
+	VarietyId            string
+	VarietyCode          string
+	VarietyName          string
+	WeekTime             string
+	WeekStart            string
+	WeekEnd              string
+	BaseDate             string
+	MonthlyPriceForecast string
+	WeeklyUpForecast     string
+	WeeklyDownForecast   string
+	Status               string
+	SubmitTime           string
+	CreateTime           string
+	ModifyTime           string
+}
+
+func (m *AssessmentForm) Cols() AssessmentFormCols {
+	return AssessmentFormCols{
+		PrimaryId:            "assessment_form_id",
+		FormCode:             "form_code",
+		ResearcherId:         "researcher_id",
+		ResearcherAdminId:    "researcher_admin_id",
+		ResearcherName:       "researcher_name",
+		VarietyId:            "variety_id",
+		VarietyCode:          "variety_code",
+		VarietyName:          "variety_name",
+		WeekTime:             "week_time",
+		WeekStart:            "week_start",
+		WeekEnd:              "week_end",
+		BaseDate:             "base_date",
+		MonthlyPriceForecast: "monthly_price_forecast",
+		WeeklyUpForecast:     "weekly_up_forecast",
+		WeeklyDownForecast:   "weekly_down_forecast",
+		Status:               "status",
+		SubmitTime:           "submit_time",
+		CreateTime:           "create_time",
+		ModifyTime:           "modify_time",
+	}
+}
+
+func (m *AssessmentForm) Create() (err error) {
+	err = global.DEFAULT_DB.Create(m).Error
+	return
+}
+
+func (m *AssessmentForm) Update(cols []string) (err error) {
+	err = global.DEFAULT_DB.Select(cols).Updates(m).Error
+	return
+}
+
+func (m *AssessmentForm) Remove() (err error) {
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.Cols().PrimaryId)
+	err = global.DEFAULT_DB.Exec(sql, m.AssessmentFormId).Error
+	return
+}
+
+func (m *AssessmentForm) MultiRemove(ids []int) (err error) {
+	if len(ids) == 0 {
+		return
+	}
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s IN (%s)`, m.TableName(), m.Cols().PrimaryId, utils.GetOrmInReplace(len(ids)))
+	err = global.DEFAULT_DB.Exec(sql, ids).Error
+	return
+}
+
+func (m *AssessmentForm) RemoveByCondition(condition string, pars []interface{}) (err error) {
+	if condition == "" {
+		return
+	}
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s`, m.TableName(), condition)
+	err = global.DEFAULT_DB.Exec(sql, pars...).Error
+	return
+}
+
+func (m *AssessmentForm) GetItemById(primaryId int) (item *AssessmentForm, err error) {
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.Cols().PrimaryId)
+	err = global.DEFAULT_DB.Raw(sql, primaryId).First(&item).Error
+	return
+}
+
+func (m *AssessmentForm) GetItemByCondition(condition string, pars []interface{}, orderRule string) (item *AssessmentForm, err error) {
+	order := ``
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s %s LIMIT 1`, m.TableName(), condition, order)
+	err = global.DEFAULT_DB.Raw(sql, pars...).First(&item).Error
+	return
+}
+
+func (m *AssessmentForm) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = global.DEFAULT_DB.Raw(sql, pars...).Scan(&count).Error
+	return
+}
+
+func (m *AssessmentForm) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*AssessmentForm, err error) {
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := fmt.Sprintf(`ORDER BY %s DESC`, m.Cols().CreateTime)
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s`, fields, m.TableName(), condition, order)
+	err = global.DEFAULT_DB.Raw(sql, pars...).Find(&items).Error
+	return
+}
+
+func (m *AssessmentForm) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*AssessmentForm, err error) {
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := fmt.Sprintf(`ORDER BY %s DESC`, m.Cols().CreateTime)
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s LIMIT ?,?`, fields, m.TableName(), condition, order)
+	pars = append(pars, startSize, pageSize)
+	err = global.DEFAULT_DB.Raw(sql, pars...).Find(&items).Error
+	return
+}
+
+// AssessmentFormDetail 填报单信息
+type AssessmentFormDetail struct {
+	//AssessmentFormId int `description:"填报单ID"`
+	//FormCode string `description:"单号"`
+	//ResearcherId         int    `description:"研究员ID"`
+	//ResearcherAdminId    int    `description:"研究员用户ID"`
+	ResearcherName string `description:"研究员姓名" json:"researcherName"`
+	//VarietyId            int    `description:"品种ID"`
+	VarietyCode          string `description:"品种编码" json:"varietyCode"`
+	VarietyName          string `description:"品种名称" json:"varietyName"`
+	WeekTime             string `description:"周度(格式:202501,202502)" json:"weekTime"`
+	BaseDate             string `description:"价格基准日期" json:"baseDate"`
+	MonthlyPriceForecast string `description:"月度涨跌:涨/跌/震荡" json:"monthlyPriceForecast"`
+	WeeklyUpForecast     string `description:"周度上行风险:是/否" json:"weeklyUpForecast"`
+	WeeklyDownForecast   string `description:"周度下行风险:是/否" json:"weeklyDownForecast"`
+	//Status               int    `description:"状态:0-草稿;1-已提交;"`
+	SubmitTime string `description:"提交时间" json:"submitTime"`
+	CreateTime string `description:"创建时间" json:"createTime"`
+	ModifyTime string `description:"修改时间" json:"modifyTime"`
+	//Button               AssessmentFormButton `description:"按钮权限"`
+}
+
+type AssessmentFormButton struct {
+	ViewButton   bool `description:"查看按钮"`
+	EditButton   bool `description:"编辑按钮"`
+	RemoveButton bool `description:"删除按钮"`
+	SubmitButton bool `description:"提交按钮"`
+	CancelButton bool `description:"撤销按钮"`
+}
+
+func (m *AssessmentForm) Format2Detail() (item *AssessmentFormDetail) {
+	item = new(AssessmentFormDetail)
+	//item.AssessmentFormId = m.AssessmentFormId
+	//item.FormCode = m.FormCode
+	//item.ResearcherId = m.ResearcherId
+	//item.ResearcherAdminId = m.ResearcherAdminId
+	item.ResearcherName = m.ResearcherName
+	//item.VarietyId = m.VarietyId
+	item.VarietyCode = m.VarietyCode
+	item.VarietyName = m.VarietyName
+	item.WeekTime = m.WeekTime
+	item.BaseDate = utils.TimeTransferString(utils.FormatDate, m.BaseDate)
+	item.MonthlyPriceForecast = m.MonthlyPriceForecast
+	item.WeeklyUpForecast = m.WeeklyUpForecast
+	item.WeeklyDownForecast = m.WeeklyDownForecast
+	//item.Status = m.Status
+	item.SubmitTime = utils.TimeTransferString(utils.FormatDate, m.SubmitTime)
+	item.CreateTime = utils.TimeTransferString(utils.FormatDateTime, m.CreateTime)
+	item.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, m.ModifyTime)
+	return
+}
+
+// AssessmentFormViewResp 查看填报单
+type AssessmentFormViewResp struct {
+	List      []*AssessmentFormDetail `description:"填报单详情" json:"dataList"`
+	Status    int                     `description:"状态" json:"status"`
+	OutStatus int                     `description:"外部状态" json:"outStatus"`
+	OutNo     string                  `description:"外部单号" json:"outNo"`
+	DataId    string                  `description:"单号" json:"dataId"`
+	Head      AssessmentFormDetail    `description:"表头信息" json:"head"`
+}
+
+// UpdateOutStatusByFormCode
+// @Description: 根据填报单号更新外部单号和外部状态
+// @author: Roc
+// @receiver m
+// @datetime 2025-06-26 17:38:59
+// @param formCode string
+// @param outNo string
+// @param outStatus int
+// @return err error
+func (m *AssessmentForm) UpdateOutStatusByFormCode(formCode, outNo string, outStatus int) (err error) {
+	sql := fmt.Sprintf(`UPDATE %s SET out_status = ?,out_no = ? WHERE %s = ? `, m.TableName(), m.Cols().FormCode)
+	err = global.DEFAULT_DB.Exec(sql, outStatus, outNo, formCode).Error
+
+	return
+}

+ 198 - 0
models/assessment_researcher.go

@@ -0,0 +1,198 @@
+package models
+
+import (
+	"eta/eta_hub/global"
+	"eta/eta_hub/utils"
+	"fmt"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"gorm.io/gorm"
+	"strings"
+	"time"
+)
+
+const (
+	AssessmentResearcherDisabled = 0
+	AssessmentResearcherEnabled  = 1
+)
+
+// AssessmentResearcher 考核研究员表
+type AssessmentResearcher struct {
+	AssessmentResearcherId int       `gorm:"column:assessment_researcher_id;primaryKey;autoIncrement"`
+	AdminId                int       `description:"系统用户ID"`
+	RealName               string    `description:"用户姓名"`
+	Enabled                int       `description:"状态:0-禁用;1-正常"`
+	CreateTime             time.Time `description:"创建时间"`
+	ModifyTime             time.Time `description:"修改时间"`
+}
+
+func (m *AssessmentResearcher) TableName() string {
+	return "assessment_researcher"
+}
+
+type AssessmentResearcherCols struct {
+	PrimaryId  string
+	AdminId    string
+	RealName   string
+	Enabled    string
+	CreateTime string
+	ModifyTime string
+}
+
+func (m *AssessmentResearcher) Cols() AssessmentResearcherCols {
+	return AssessmentResearcherCols{
+		PrimaryId:  "assessment_researcher_id",
+		AdminId:    "admin_id",
+		RealName:   "real_name",
+		Enabled:    "enabled",
+		CreateTime: "create_time",
+		ModifyTime: "modify_time",
+	}
+}
+
+func (m *AssessmentResearcher) Create() (err error) {
+	err = global.DEFAULT_DB.Create(m).Error
+	return
+}
+
+func (m *AssessmentResearcher) Update(cols []string) (err error) {
+	err = global.DEFAULT_DB.Select(cols).Updates(m).Error
+	return
+}
+
+func (m *AssessmentResearcher) Remove() (err error) {
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.Cols().PrimaryId)
+	err = global.DEFAULT_DB.Exec(sql, m.AssessmentResearcherId).Error
+	return
+}
+
+func (m *AssessmentResearcher) MultiRemove(ids []int) (err error) {
+	if len(ids) == 0 {
+		return
+	}
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s IN (%s)`, m.TableName(), m.Cols().PrimaryId, utils.GetOrmInReplace(len(ids)))
+	err = global.DEFAULT_DB.Exec(sql, ids).Error
+	return
+}
+
+func (m *AssessmentResearcher) RemoveByCondition(condition string, pars []interface{}) (err error) {
+	if condition == "" {
+		return
+	}
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s`, m.TableName(), condition)
+	err = global.DEFAULT_DB.Exec(sql, pars...).Error
+	return
+}
+
+func (m *AssessmentResearcher) GetItemById(primaryId int) (item *AssessmentResearcher, err error) {
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.Cols().PrimaryId)
+	err = global.DEFAULT_DB.Raw(sql, primaryId).First(&item).Error
+	return
+}
+
+func (m *AssessmentResearcher) GetItemByCondition(condition string, pars []interface{}, orderRule string) (item *AssessmentResearcher, err error) {
+	order := ``
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s %s LIMIT 1`, m.TableName(), condition, order)
+	err = global.DEFAULT_DB.Raw(sql, pars...).First(&item).Error
+	return
+}
+
+func (m *AssessmentResearcher) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = global.DEFAULT_DB.Raw(sql, pars...).Scan(&count).Error
+	return
+}
+
+func (m *AssessmentResearcher) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*AssessmentResearcher, err error) {
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := fmt.Sprintf(`ORDER BY %s DESC`, m.Cols().CreateTime)
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s`, fields, m.TableName(), condition, order)
+	err = global.DEFAULT_DB.Raw(sql, pars...).Find(&items).Error
+	return
+}
+
+func (m *AssessmentResearcher) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*AssessmentResearcher, err error) {
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := fmt.Sprintf(`ORDER BY %s DESC`, m.Cols().CreateTime)
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s LIMIT ?,?`, fields, m.TableName(), condition, order)
+	pars = append(pars, startSize, pageSize)
+	err = global.DEFAULT_DB.Raw(sql, pars...).Find(&items).Error
+	return
+}
+
+// AfterUpdate 同步更新其他表冗余
+func (m *AssessmentResearcher) AfterUpdate(tx *gorm.DB) (err error) {
+	if tx.Statement.Changed(m.Cols().RealName) {
+		fmt.Println(111)
+		formOb := new(AssessmentForm)
+		err = tx.Model(formOb).Where(fmt.Sprintf("%s = ?", formOb.Cols().ResearcherId), m.AssessmentResearcherId).Update(formOb.Cols().ResearcherName, m.RealName).Error
+		fmt.Println("AfterUpdate", err)
+	}
+	return
+}
+
+// AssessmentResearcherAddReq 新增研究员请求
+type AssessmentResearcherAddReq struct {
+	AdminId            int   `description:"用户ID"`
+	ViewAdminIds       []int `description:"查看权限用户IDs"`
+	AssessmentAdminIds []int `description:"统计权限用户IDs"`
+}
+
+// AssessmentResearcherEditReq 编辑研究员请求
+type AssessmentResearcherEditReq struct {
+	AssessmentResearcherId int `description:"研究员ID"`
+	AssessmentResearcherAddReq
+}
+
+// AssessmentResearcherRemoveReq 删除研究员请求
+type AssessmentResearcherRemoveReq struct {
+	AssessmentResearcherId int `description:"研究员ID"`
+}
+
+// AssessmentResearcherPageListReq 研究员列表
+type AssessmentResearcherPageListReq struct {
+	PageSize                int    `form:"PageSize"`
+	CurrentIndex            int    `form:"CurrentIndex"`
+	RealName                string `form:"RealName" description:"用户姓名"`
+	AssessmentResearcherIds string `form:"AssessmentResearcherIds" description:"研究员IDs"`
+	OnlyEnabled             bool   `form:"OnlyEnabled" description:"是否仅显示启用:true-是"`
+	ResearcherAuthType      int    `form:"ResearcherAuthType" description:"权限类型:0-全部;1-当前用户有查看权限的;2-当前用户有统计权限的"`
+}
+
+// AssessmentResearcherPageListResp 研究员列表响应
+type AssessmentResearcherPageListResp struct {
+	List   []*AssessmentResearcherDetail
+	Paging *paging.PagingItem `description:"分页数据"`
+}
+
+type AssessmentResearcherDetail struct {
+	AssessmentResearcherId int                          `description:"研究员ID"`
+	AdminId                int                          `description:"用户ID"`
+	RealName               string                       `description:"用户姓名"`
+	Enabled                int                          `description:"状态:0-禁用;1-正常"`
+	ViewAdmins             []AssessmentResearcherDetail `description:"查看权限用户"`
+	AssessmentAdmins       []AssessmentResearcherDetail `description:"统计权限用户"`
+}
+
+func (m *AssessmentResearcher) Format2Detail() (item *AssessmentResearcherDetail) {
+	item = new(AssessmentResearcherDetail)
+	item.AssessmentResearcherId = m.AssessmentResearcherId
+	item.AdminId = m.AdminId
+	item.RealName = m.RealName
+	item.Enabled = m.Enabled
+	return
+}

+ 106 - 0
models/assessment_researcher_variety_mapping.go

@@ -0,0 +1,106 @@
+package models
+
+import (
+	"eta/eta_hub/global"
+	"eta/eta_hub/utils"
+	"fmt"
+	"strings"
+)
+
+// AssessmentResearcherVarietyMapping 研究员考核-用户权限关系表
+type AssessmentResearcherVarietyMapping struct {
+	Id                     int `gorm:"column:id;primaryKey;autoIncrement"`
+	AssessmentResearcherId int `description:"研究员ID"`
+	AdminId                int `description:"系统用户ID"`
+	VarietyId              int `description:"品种ID"`
+}
+
+func (m *AssessmentResearcherVarietyMapping) TableName() string {
+	return "assessment_researcher_variety_mapping"
+}
+
+type AssessmentResearcherVarietyMappingCols struct {
+	PrimaryId              string
+	AssessmentResearcherId string
+	AdminId                string
+	VarietyId              string
+}
+
+func (m *AssessmentResearcherVarietyMapping) Cols() AssessmentResearcherVarietyMappingCols {
+	return AssessmentResearcherVarietyMappingCols{
+		PrimaryId:              "id",
+		AssessmentResearcherId: "assessment_researcher_id",
+		AdminId:                "admin_id",
+		VarietyId:              "variety_id",
+	}
+}
+
+func (m *AssessmentResearcherVarietyMapping) Create() (err error) {
+	err = global.DEFAULT_DB.Create(m).Error
+	return
+}
+
+func (m *AssessmentResearcherVarietyMapping) Update(cols []string) (err error) {
+	err = global.DEFAULT_DB.Select(cols).Updates(m).Error
+	return
+}
+
+func (m *AssessmentResearcherVarietyMapping) Remove() (err error) {
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.Cols().PrimaryId)
+	err = global.DEFAULT_DB.Exec(sql, m.Id).Error
+	return
+}
+
+func (m *AssessmentResearcherVarietyMapping) MultiRemove(ids []int) (err error) {
+	if len(ids) == 0 {
+		return
+	}
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s IN (%s)`, m.TableName(), m.Cols().PrimaryId, utils.GetOrmInReplace(len(ids)))
+	err = global.DEFAULT_DB.Exec(sql, ids).Error
+	return
+}
+
+func (m *AssessmentResearcherVarietyMapping) RemoveByCondition(condition string, pars []interface{}) (err error) {
+	if condition == "" {
+		return
+	}
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s`, m.TableName(), condition)
+	err = global.DEFAULT_DB.Exec(sql, pars...).Error
+	return
+}
+
+func (m *AssessmentResearcherVarietyMapping) GetItemById(primaryId int) (item *AssessmentResearcherVarietyMapping, err error) {
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.Cols().PrimaryId)
+	err = global.DEFAULT_DB.Raw(sql, primaryId).First(&item).Error
+	return
+}
+
+func (m *AssessmentResearcherVarietyMapping) GetItemByCondition(condition string, pars []interface{}, orderRule string) (err error) {
+	order := ``
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s %s LIMIT 1`, m.TableName(), condition, order)
+	err = global.DEFAULT_DB.Raw(sql, pars...).First(&m).Error
+	return
+}
+
+func (m *AssessmentResearcherVarietyMapping) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*AssessmentResearcherVarietyMapping, err error) {
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := ``
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s`, fields, m.TableName(), condition, order)
+	err = global.DEFAULT_DB.Raw(sql, pars...).Find(&items).Error
+	return
+}
+
+func (m *AssessmentResearcherVarietyMapping) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = global.DEFAULT_DB.Raw(sql, pars...).Scan(&count).Error
+	return
+}

+ 20 - 0
models/system/sys_user.go

@@ -43,6 +43,7 @@ type Admin struct {
 	CityCode                  string    `description:"市编码"`
 	EmployeeId                string    `description:"员工工号(钉钉/每刻报销)"`
 	TelAreaCode               string    `description:"手机区号"`
+	OutId                     string    `description:"外部ID"`
 }
 
 type AdminItem struct {
@@ -82,6 +83,7 @@ type AdminItem struct {
 	CityCode                  string `json:"-" description:"市编码"`
 	EmployeeId                string `json:"-" description:"员工工号(钉钉/每刻报销)"`
 	TelAreaCode               string `description:"手机区号"`
+	OutId                     string `description:"外部ID"`
 }
 
 // GetSysUserByAdminName 账号获取用户
@@ -112,6 +114,24 @@ func GetUserByAdminName(adminName string) (item *AdminItem, err error) {
 	return item, err
 }
 
+// GetUserByOutId
+// @Description: 根据外部ID获取用户信息
+// @author: Roc
+// @datetime 2025-06-26 11:26:28
+// @param outId string
+// @return item *AdminItem
+// @return err error
+func GetUserByOutId(outId string) (item *AdminItem, err error) {
+	sql := `SELECT * FROM admin WHERE out_id != '' AND out_id = ? LIMIT 1`
+	err = global.DEFAULT_DB.Raw(sql, outId).First(&item).Error
+
+	if err != nil && err.Error() == utils.ErrNoRow() {
+		return nil, nil
+	}
+
+	return item, err
+}
+
 type UserCheckResp struct {
 	IsUser    bool
 	AdminInfo *AdminItem

+ 22 - 0
models/xy/base.go

@@ -0,0 +1,22 @@
+package xy
+
+type BaseResponse struct {
+	//Ret          int
+	ErrMsg string `json:"-"`
+	//ErrCode      string
+	Data       interface{} `json:"returnObject"`
+	ReturnCode string      `json:"returnCode"`
+	Msg        string      `json:"returnMsg"`
+	Status     string      `json:"status"`
+}
+
+func (r *BaseResponse) Init() *BaseResponse {
+	return &BaseResponse{ReturnCode: `E`, Msg: `失败`, Status: `E`}
+}
+
+type BaseRequest struct {
+}
+
+func (br *BaseRequest) Init() *BaseRequest {
+	return &BaseRequest{}
+}

+ 31 - 0
models/xy/request.go

@@ -0,0 +1,31 @@
+package xy
+
+type BaseXyReq struct {
+	Source  string `json:"source"`
+	Target  string `json:"target"`
+	DocCode string `json:"docCode"`
+}
+
+type AssessmentFormQueryReq struct {
+	In0       BaseXyReq `json:"in0"`
+	Loginid   string    `json:"loginid"`
+	StartTime string    `json:"startTime"`
+	EndTime   string    `json:"endTime"`
+}
+
+// AssessmentCheckReq
+// @Description: 确认用户是有权限(需要)填报请求
+type AssessmentCheckReq struct {
+	In0      BaseXyReq `json:"in0"`
+	Loginid  string    `json:"loginid"`
+	PageCode string    `json:"pageCode"`
+}
+
+// UpdateAssessmentFormReq
+// @Description: 更新填报单外部状态
+type UpdateAssessmentFormReq struct {
+	In0      BaseXyReq `json:"in0"`
+	ReportId string    `json:"reportId"`
+	DataId   string    `json:"dataId"`
+	Status   string    `json:"status"`
+}

+ 8 - 0
models/xy/response.go

@@ -0,0 +1,8 @@
+package xy
+
+// AssessmentCheckResp
+// @Description: 确认用户是有权限(需要)填报返回
+type AssessmentCheckResp struct {
+	DataAuth   bool `description:"是否有权限(是否需要)填报" json:"dataAuthority"`
+	SubmitAuth bool `description:"是否有权限(是否需要)填报" json:"submitAuthority"`
+}

+ 27 - 0
routers/commentsRouter.go

@@ -7,6 +7,33 @@ import (
 
 func init() {
 
+    beego.GlobalControllerRouter["eta/eta_hub/controllers/xy:AssessmentFormController"] = append(beego.GlobalControllerRouter["eta/eta_hub/controllers/xy:AssessmentFormController"],
+        beego.ControllerComments{
+            Method: "Detail",
+            Router: `/forms/detail`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_hub/controllers/xy:AssessmentFormController"] = append(beego.GlobalControllerRouter["eta/eta_hub/controllers/xy:AssessmentFormController"],
+        beego.ControllerComments{
+            Method: "UpdateOutStatus",
+            Router: `/forms/update`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_hub/controllers/xy:AssessmentFormController"] = append(beego.GlobalControllerRouter["eta/eta_hub/controllers/xy:AssessmentFormController"],
+        beego.ControllerComments{
+            Method: "Check",
+            Router: `/user/check`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_hub/controllers:BusinessChartController"] = append(beego.GlobalControllerRouter["eta/eta_hub/controllers:BusinessChartController"],
         beego.ControllerComments{
             Method: "ChartDetail",

+ 6 - 0
routers/router.go

@@ -9,6 +9,7 @@ package routers
 
 import (
 	"eta/eta_hub/controllers"
+	"eta/eta_hub/controllers/xy"
 	"github.com/beego/beego/v2/server/web"
 	"github.com/beego/beego/v2/server/web/filter/cors"
 )
@@ -93,6 +94,11 @@ func init() {
 				&controllers.BusinessChartController{},
 			),
 		),
+		web.NSNamespace("/assessment_form",
+			web.NSInclude(
+				&xy.AssessmentFormController{},
+			),
+		),
 	)
 	web.AddNamespace(ns)
 }

+ 88 - 0
utils/common.go

@@ -12,6 +12,7 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
+	"gorm.io/gorm"
 	"image"
 	"image/png"
 	"io"
@@ -1192,3 +1193,90 @@ func DateConvMysqlConvMongo(dateCon string) string {
 func GetCurrentTime() string {
 	return time.Now().Format("2006-01-02 15:04:05")
 }
+
+// GetAssessmentWeekAndFriday 获取给定日期的周数,以及所在周的周五
+// 规则:某年的第一个周五所在周为该年的第一周
+// 返回:格式化字符串(如"202504")、周数(如4)和所在周的周五日期
+func GetAssessmentWeekAndFriday(t time.Time) (string, int, time.Time) {
+	year := t.Year()
+
+	// 找到该年第一个周五的日期
+	firstFriday := findFirstFriday(year)
+
+	// 计算当前日期与该年第一个周五所在周的第一天(周一)的天数差
+	daysSinceFirstWeek := t.YearDay() - firstFriday.YearDay() + int(firstFriday.Weekday()) - int(time.Monday)
+
+	weekNum := 1
+	if daysSinceFirstWeek > 0 {
+		weekNum += daysSinceFirstWeek / 7
+		if daysSinceFirstWeek%7 != 0 {
+			weekNum++
+		}
+	} else {
+		// 如果当前日期在第一个周五所在周之前,则属于上一年的最后一周
+		prevYear := year - 1
+		prevFirstFriday := findFirstFriday(prevYear)
+		daysInPrevYear := daysInYear(prevYear)
+
+		daysSincePrevFirstWeek := t.YearDay() + (daysInPrevYear - prevFirstFriday.YearDay()) + int(prevFirstFriday.Weekday()) - int(time.Monday)
+		weekNum = daysSincePrevFirstWeek / 7
+		if daysSincePrevFirstWeek%7 != 0 {
+			weekNum++
+		}
+		year = prevYear
+	}
+
+	// 计算当前日期所在周的周五
+	currentWeekFriday := findFridayOfWeek(t)
+
+	// 格式化输出
+	formatted := fmt.Sprintf("%04d%02d", year, weekNum)
+	return formatted, weekNum, currentWeekFriday
+}
+
+// findFirstFriday 找到某年的第一个周五
+func findFirstFriday(year int) time.Time {
+	// 从1月1日开始找
+	date := time.Date(year, time.January, 1, 0, 0, 0, 0, time.UTC)
+
+	// 找到第一个周五
+	for date.Weekday() != time.Friday {
+		date = date.AddDate(0, 0, 1)
+	}
+
+	return date
+}
+
+// findFridayOfWeek 找到给定日期所在周的周五
+func findFridayOfWeek(t time.Time) time.Time {
+	// 获取当前日期是周几 (0=周日, 1=周一, ..., 6=周六)
+	weekday := int(t.Weekday())
+
+	// 计算到周五的天数差 (周五是5)
+	daysToFriday := (5 - weekday + 7) % 7
+
+	// 如果当前就是周五,daysToFriday会是0
+	if daysToFriday < 0 {
+		daysToFriday += 7
+	}
+
+	return t.AddDate(0, 0, daysToFriday)
+}
+
+// daysInYear 计算某年有多少天
+func daysInYear(year int) int {
+	first := time.Date(year, time.January, 1, 0, 0, 0, 0, time.UTC)
+	last := time.Date(year, time.December, 31, 0, 0, 0, 0, time.UTC)
+	return last.YearDay() - first.YearDay() + 1
+}
+
+// IsErrNoRow
+// @Description: 判断是否是gorm的查询不到数据的报错
+// @param err
+// @return bool
+func IsErrNoRow(err error) bool {
+	if err == nil {
+		return false
+	}
+	return errors.Is(err, gorm.ErrRecordNotFound)
+}