297 lines
7.2 KiB
Go
297 lines
7.2 KiB
Go
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})
|
|
}
|