package controllers import ( "encoding/json" "io" "strconv" "strings" "server/pkg/identity" "server/pkg/jwtutil" "server/services/dailyreport" beego "github.com/beego/beego/v2/server/web" ) // BackendOaDailyReportController OA工作日报(后台 PC 端)。 // // 可见范围由 services/dailyreport 统一判定: // - 本人:自己的全部日报(含草稿),补写窗口内可编辑/删除; // - 部门主管:本人作为负责人的部门及其下级部门成员「已提交」的日报; // - 租户管理员:全租户日报,可删除任意日报与评论。 type BackendOaDailyReportController struct { beego.Controller } func (c *BackendOaDailyReportController) odrClaims() (*jwtutil.Claims, error) { return identity.Claims(c.Ctx.Request.Header.Get("Authorization"), identity.EndBackend) } func (c *BackendOaDailyReportController) odrErr(status, bizCode int, msg string) { c.Ctx.Output.SetStatus(status) c.Data["json"] = map[string]interface{}{"code": bizCode, "msg": msg} _ = c.ServeJSON() } func (c *BackendOaDailyReportController) odrOk(data interface{}) { c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": data} _ = c.ServeJSON() } func (c *BackendOaDailyReportController) odrFail(err error) { code := dailyreport.StatusOf(err, 500) msg := err.Error() if code == 500 { msg = "操作失败,请稍后重试" } c.odrErr(code, code, msg) } // odrCtx 解析令牌并组装日报身份;失败时已输出响应。 func (c *BackendOaDailyReportController) odrCtx() (*dailyreport.UserContext, bool) { claims, err := c.odrClaims() if err != nil { code := identity.Status(err) c.odrErr(code, code, err.Error()) return nil, false } u, err := dailyreport.BuildContext( uint64(claims.TenantId), uint64(claims.UserID), claims.Username, claims.UserType == "platform") if err != nil { c.odrFail(err) return nil, false } return u, true } func (c *BackendOaDailyReportController) odrIDParam(name string) uint64 { id, _ := strconv.ParseUint(c.Ctx.Input.Param(name), 10, 64) if id == 0 { c.odrErr(400, 400, "无效ID") } return id } // Profile GET /backend/oa/dailyreport/profile // 返回当前用户身份、补写窗口与团队可见性,供页面决定是否显示「团队日报」Tab。 func (c *BackendOaDailyReportController) Profile() { u, ok := c.odrCtx() if !ok { return } teamOrgs := u.TeamOrgIDs if teamOrgs == nil { teamOrgs = []uint64{} } c.odrOk(map[string]interface{}{ "user_id": u.UID, "user_name": u.Name, "org_id": u.OrgID, "org_name": u.OrgName, "is_admin": u.IsAdmin, "can_view_team": u.CanViewTeam, "team_org_ids": teamOrgs, "window_start": dailyreport.WindowStart(), "window_end": dailyreport.Today(), "backfill_days": dailyreport.BackfillDays, }) } // MineList GET /backend/oa/dailyreport/mine/list // 我的日报分页列表,支持 start_date/end_date 筛选。 func (c *BackendOaDailyReportController) MineList() { u, ok := c.odrCtx() if !ok { return } page, _ := c.GetInt("page", 1) pageSize, _ := c.GetInt("pageSize", 20) res, err := dailyreport.ListMine(u, dailyreport.MineQuery{ StartDate: strings.TrimSpace(c.GetString("start_date")), EndDate: strings.TrimSpace(c.GetString("end_date")), Page: page, PageSize: pageSize, }) if err != nil { c.odrFail(err) return } c.odrOk(res) } // Save POST /backend/oa/dailyreport/save // 新建或更新日报;submit=true 提交,false 存草稿。 func (c *BackendOaDailyReportController) Save() { u, ok := c.odrCtx() if !ok { return } var in dailyreport.SaveInput raw, err := io.ReadAll(c.Ctx.Request.Body) if err != nil || json.Unmarshal(raw, &in) != nil { c.odrErr(400, 400, "参数错误") return } item, err := dailyreport.Save(u, in) if err != nil { c.odrFail(err) return } c.odrOk(item) } // Detail GET /backend/oa/dailyreport/detail/:id func (c *BackendOaDailyReportController) Detail() { u, ok := c.odrCtx() if !ok { return } id := c.odrIDParam(":id") if id == 0 { return } detail, err := dailyreport.GetDetail(u, id) if err != nil { c.odrFail(err) return } c.odrOk(detail) } // Delete DELETE /backend/oa/dailyreport/delete/:id func (c *BackendOaDailyReportController) Delete() { u, ok := c.odrCtx() if !ok { return } id := c.odrIDParam(":id") if id == 0 { return } if err := dailyreport.Delete(u, id); err != nil { c.odrFail(err) return } c.odrOk(map[string]interface{}{"id": id}) } // Withdraw POST /backend/oa/dailyreport/withdraw/:id // 撤销提交:已提交日报退回草稿并留存本次提交的内容快照,改完需重新提交。 func (c *BackendOaDailyReportController) Withdraw() { u, ok := c.odrCtx() if !ok { return } id := c.odrIDParam(":id") if id == 0 { return } item, err := dailyreport.Withdraw(u, id) if err != nil { c.odrFail(err) return } c.odrOk(item) } // Calendar GET /backend/oa/dailyreport/calendar // 我的日报近期热力图:months 默认 3,返回区间内逐日状态(草稿/已提交),供前端按月、按周渲染。 func (c *BackendOaDailyReportController) Calendar() { u, ok := c.odrCtx() if !ok { return } months, _ := c.GetInt("months", 3) res, err := dailyreport.Calendar(u, months) if err != nil { c.odrFail(err) return } c.odrOk(res) } // TeamStatus GET /backend/oa/dailyreport/team/status // 团队日报汇总:date/org_id/keyword/page/pageSize。 func (c *BackendOaDailyReportController) TeamStatus() { u, ok := c.odrCtx() if !ok { return } page, _ := c.GetInt("page", 1) pageSize, _ := c.GetInt("pageSize", 20) orgID, _ := c.GetInt64("org_id", 0) res, err := dailyreport.TeamStatus(u, dailyreport.TeamQuery{ Date: strings.TrimSpace(c.GetString("date")), OrgID: uint64(orgID), Keyword: strings.TrimSpace(c.GetString("keyword")), Page: page, PageSize: pageSize, }) if err != nil { c.odrFail(err) return } c.odrOk(res) } // TeamOrgs GET /backend/oa/dailyreport/team/orgs func (c *BackendOaDailyReportController) TeamOrgs() { u, ok := c.odrCtx() if !ok { return } orgs, err := dailyreport.TeamOrgs(u) if err != nil { c.odrFail(err) return } c.odrOk(map[string]interface{}{"list": orgs}) } type oaDailyReportCommentPayload struct { ReportID uint64 `json:"report_id"` Content string `json:"content"` ReplyToID uint64 `json:"reply_to_id"` } // Comment POST /backend/oa/dailyreport/comment func (c *BackendOaDailyReportController) Comment() { u, ok := c.odrCtx() if !ok { return } var payload oaDailyReportCommentPayload raw, err := io.ReadAll(c.Ctx.Request.Body) if err != nil || json.Unmarshal(raw, &payload) != nil { c.odrErr(400, 400, "参数错误") return } if payload.ReportID == 0 { c.odrErr(400, 400, "缺少日报ID") return } comment, err := dailyreport.AddComment(u, payload.ReportID, payload.Content, payload.ReplyToID) if err != nil { c.odrFail(err) return } c.odrOk(comment) } // DeleteComment DELETE /backend/oa/dailyreport/comment/:id func (c *BackendOaDailyReportController) DeleteComment() { u, ok := c.odrCtx() if !ok { return } id := c.odrIDParam(":id") if id == 0 { return } if err := dailyreport.DeleteComment(u, id); err != nil { c.odrFail(err) return } c.odrOk(map[string]interface{}{"id": id}) }