完善uniapp
This commit is contained in:
@@ -0,0 +1,178 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"server/models"
|
||||
"server/pkg/jwtutil"
|
||||
"strings"
|
||||
|
||||
"github.com/beego/beego/v2/client/orm"
|
||||
beego "github.com/beego/beego/v2/server/web"
|
||||
)
|
||||
|
||||
type AppActivityController struct {
|
||||
beego.Controller
|
||||
}
|
||||
|
||||
func (c *AppActivityController) activityClaims() (*jwtutil.Claims, error) {
|
||||
auth := c.Ctx.Request.Header.Get("Authorization")
|
||||
if auth == "" {
|
||||
return nil, orm.ErrNoRows
|
||||
}
|
||||
parts := strings.SplitN(auth, " ", 2)
|
||||
if len(parts) != 2 || parts[0] != "Bearer" {
|
||||
return nil, orm.ErrNoRows
|
||||
}
|
||||
claims, err := jwtutil.ParseToken(parts[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if claims.UserType != "backend" && claims.UserType != "app" && claims.UserType != "platform" {
|
||||
return nil, orm.ErrNoRows
|
||||
}
|
||||
return claims, nil
|
||||
}
|
||||
|
||||
// GetList GET /app/activity/list
|
||||
func (c *AppActivityController) GetList() {
|
||||
claims, err := c.activityClaims()
|
||||
if err != nil {
|
||||
c.Ctx.Output.SetStatus(401)
|
||||
c.Data["json"] = map[string]interface{}{"code": 401, "msg": "未登录"}
|
||||
_ = c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
limit, _ := c.GetInt("limit", 10)
|
||||
if limit < 1 || limit > 50 {
|
||||
limit = 10
|
||||
}
|
||||
|
||||
var logs []models.SystemOperationLog
|
||||
qs := models.Orm.QueryTable(new(models.SystemOperationLog)).
|
||||
Filter("user_id", claims.UserID).
|
||||
Exclude("action__in", "登录", "退出", "查询").
|
||||
OrderBy("-create_time").
|
||||
Limit(limit)
|
||||
_, err = qs.All(&logs)
|
||||
if err != nil && err != orm.ErrNoRows {
|
||||
c.Ctx.Output.SetStatus(500)
|
||||
c.Data["json"] = map[string]interface{}{"code": 500, "msg": "查询失败"}
|
||||
_ = c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
type activityItem struct {
|
||||
ID uint64 `json:"id"`
|
||||
Action string `json:"action"`
|
||||
TargetType string `json:"target_type"`
|
||||
Title string `json:"title"`
|
||||
URL string `json:"url"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
}
|
||||
|
||||
list := make([]activityItem, 0, len(logs))
|
||||
for _, l := range logs {
|
||||
module := l.Module
|
||||
action := l.Action
|
||||
|
||||
// 从请求体中提取标题
|
||||
title := extractTitle(l.Module, l.Action, l.RequestData)
|
||||
if title == "" {
|
||||
title = fmt.Sprintf("您%s了:%s", actionLabel(action), moduleLabel(module))
|
||||
}
|
||||
|
||||
list = append(list, activityItem{
|
||||
ID: l.ID,
|
||||
Action: action,
|
||||
TargetType: module,
|
||||
Title: title,
|
||||
URL: l.URL,
|
||||
CreatedAt: l.CreateTime.Format("2006-01-02 15:04:05"),
|
||||
})
|
||||
}
|
||||
|
||||
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": list}
|
||||
_ = c.ServeJSON()
|
||||
}
|
||||
|
||||
// extractTitle 从请求数据中提取可读标题
|
||||
func extractTitle(module, action string, reqData *string) string {
|
||||
if reqData == nil {
|
||||
return ""
|
||||
}
|
||||
data := strings.TrimSpace(*reqData)
|
||||
if data == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
// 解析 JSON 请求体
|
||||
var body map[string]interface{}
|
||||
if err := json.Unmarshal([]byte(data), &body); err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
moduleCN := moduleLabel(module)
|
||||
actionCN := actionLabel(action)
|
||||
|
||||
// 笔记本:取 title
|
||||
if module == "notebook" {
|
||||
if t, ok := body["title"].(string); ok && t != "" {
|
||||
return fmt.Sprintf("您%s了%s:【%s】", actionCN, moduleCN, truncateStr(t, 20))
|
||||
}
|
||||
}
|
||||
|
||||
// 日程:取 content 第一行
|
||||
if module == "schedule" {
|
||||
if c, ok := body["content"].(string); ok && c != "" {
|
||||
firstLine := strings.SplitN(c, "\n", 2)[0]
|
||||
return fmt.Sprintf("您%s了%s:【%s】", actionCN, moduleCN, truncateStr(firstLine, 20))
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Sprintf("您%s了:%s", actionCN, moduleCN)
|
||||
}
|
||||
|
||||
func truncateStr(s string, maxLen int) string {
|
||||
s = strings.TrimSpace(s)
|
||||
if len([]rune(s)) > maxLen {
|
||||
return string([]rune(s)[:maxLen]) + "..."
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func moduleLabel(m string) string {
|
||||
switch m {
|
||||
case "notebook":
|
||||
return "记事本"
|
||||
case "schedule":
|
||||
return "日程提醒"
|
||||
case "erp":
|
||||
return "ERP"
|
||||
case "file":
|
||||
return "文件"
|
||||
case "article":
|
||||
return "文章"
|
||||
default:
|
||||
if m == "" {
|
||||
return "系统"
|
||||
}
|
||||
return m
|
||||
}
|
||||
}
|
||||
|
||||
func actionLabel(a string) string {
|
||||
switch a {
|
||||
case "新增":
|
||||
return "新增"
|
||||
case "编辑":
|
||||
return "编辑"
|
||||
case "删除":
|
||||
return "删除"
|
||||
case "提交":
|
||||
return "操作"
|
||||
default:
|
||||
return a
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,376 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"server/middleware"
|
||||
"server/models"
|
||||
"server/pkg/jwtutil"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/beego/beego/v2/client/orm"
|
||||
beego "github.com/beego/beego/v2/server/web"
|
||||
)
|
||||
|
||||
type AppNotebookController struct {
|
||||
beego.Controller
|
||||
}
|
||||
|
||||
func (c *AppNotebookController) appNotebookClaims() (*jwtutil.Claims, error) {
|
||||
auth := c.Ctx.Request.Header.Get("Authorization")
|
||||
if auth == "" {
|
||||
return nil, orm.ErrNoRows
|
||||
}
|
||||
parts := strings.SplitN(auth, " ", 2)
|
||||
if len(parts) != 2 || parts[0] != "Bearer" {
|
||||
return nil, orm.ErrNoRows
|
||||
}
|
||||
claims, err := jwtutil.ParseToken(parts[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if claims.UserType != "backend" && claims.UserType != "app" && claims.UserType != "platform" {
|
||||
return nil, orm.ErrNoRows
|
||||
}
|
||||
return claims, nil
|
||||
}
|
||||
|
||||
func (c *AppNotebookController) nbJsonErr(httpStatus, bizCode int, msg string) {
|
||||
c.Ctx.Output.SetStatus(httpStatus)
|
||||
c.Data["json"] = map[string]interface{}{"code": bizCode, "msg": msg}
|
||||
_ = c.ServeJSON()
|
||||
}
|
||||
|
||||
func (c *AppNotebookController) nbOk(data interface{}) {
|
||||
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": data}
|
||||
_ = c.ServeJSON()
|
||||
}
|
||||
|
||||
// GetList GET /app/notebook/list
|
||||
func (c *AppNotebookController) GetList() {
|
||||
claims, err := c.appNotebookClaims()
|
||||
if err != nil {
|
||||
c.nbJsonErr(401, 401, "未登录或无权限")
|
||||
return
|
||||
}
|
||||
|
||||
keyword := strings.TrimSpace(c.GetString("keyword"))
|
||||
|
||||
qs := models.Orm.QueryTable(new(models.BackendNotebook)).
|
||||
Filter("is_deleted", 0).
|
||||
Filter("user_id", claims.UserID)
|
||||
|
||||
if keyword != "" {
|
||||
qs = qs.Filter("title__icontains", keyword)
|
||||
}
|
||||
|
||||
var list []models.BackendNotebook
|
||||
_, err = qs.OrderBy("-pinned", "-update_time").All(&list)
|
||||
if err != nil && err != orm.ErrNoRows {
|
||||
c.nbJsonErr(500, 500, "查询失败")
|
||||
return
|
||||
}
|
||||
if list == nil {
|
||||
list = []models.BackendNotebook{}
|
||||
}
|
||||
|
||||
type noteItem struct {
|
||||
ID uint64 `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Content string `json:"content"`
|
||||
Pinned bool `json:"pinned"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
}
|
||||
|
||||
result := make([]noteItem, 0, len(list))
|
||||
for _, n := range list {
|
||||
item := noteItem{
|
||||
ID: n.ID,
|
||||
Title: n.Title,
|
||||
Content: n.Content,
|
||||
Pinned: n.Pinned == 1,
|
||||
}
|
||||
item.CreatedAt = n.CreateTime.Format("2006-01-02 15:04:05")
|
||||
if n.UpdateTime != nil {
|
||||
item.UpdatedAt = n.UpdateTime.Format("2006-01-02 15:04:05")
|
||||
} else {
|
||||
item.UpdatedAt = item.CreatedAt
|
||||
}
|
||||
result = append(result, item)
|
||||
}
|
||||
|
||||
c.nbOk(map[string]interface{}{
|
||||
"list": result,
|
||||
"total": len(result),
|
||||
})
|
||||
}
|
||||
|
||||
// GetDetail GET /app/notebook/:id
|
||||
func (c *AppNotebookController) GetDetail() {
|
||||
claims, err := c.appNotebookClaims()
|
||||
if err != nil {
|
||||
c.nbJsonErr(401, 401, "未登录或无权限")
|
||||
return
|
||||
}
|
||||
|
||||
id, err := strconv.ParseUint(c.Ctx.Input.Param(":id"), 10, 64)
|
||||
if err != nil || id == 0 {
|
||||
c.nbJsonErr(400, 400, "无效ID")
|
||||
return
|
||||
}
|
||||
|
||||
var note models.BackendNotebook
|
||||
err = models.Orm.QueryTable(new(models.BackendNotebook)).
|
||||
Filter("id", id).
|
||||
Filter("is_deleted", 0).
|
||||
Filter("user_id", claims.UserID).
|
||||
One(¬e)
|
||||
if err != nil {
|
||||
c.nbJsonErr(404, 404, "笔记不存在")
|
||||
return
|
||||
}
|
||||
|
||||
createdAt := note.CreateTime.Format("2006-01-02 15:04:05")
|
||||
updatedAt := createdAt
|
||||
if note.UpdateTime != nil {
|
||||
updatedAt = note.UpdateTime.Format("2006-01-02 15:04:05")
|
||||
}
|
||||
|
||||
c.nbOk(map[string]interface{}{
|
||||
"id": note.ID,
|
||||
"title": note.Title,
|
||||
"content": note.Content,
|
||||
"pinned": note.Pinned == 1,
|
||||
"created_at": createdAt,
|
||||
"updated_at": updatedAt,
|
||||
})
|
||||
}
|
||||
|
||||
// Create POST /app/notebook
|
||||
func (c *AppNotebookController) Create() {
|
||||
claims, err := c.appNotebookClaims()
|
||||
if err != nil {
|
||||
c.nbJsonErr(401, 401, "未登录或无权限")
|
||||
return
|
||||
}
|
||||
|
||||
raw, err := io.ReadAll(c.Ctx.Request.Body)
|
||||
if err != nil {
|
||||
c.nbJsonErr(400, 400, "参数错误")
|
||||
return
|
||||
}
|
||||
var payload struct {
|
||||
Title string `json:"title"`
|
||||
Content string `json:"content"`
|
||||
Pinned bool `json:"pinned"`
|
||||
}
|
||||
if err := json.Unmarshal(raw, &payload); err != nil {
|
||||
c.nbJsonErr(400, 400, "参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
title := strings.TrimSpace(payload.Title)
|
||||
if title == "" {
|
||||
title = "无标题"
|
||||
}
|
||||
|
||||
userID := uint64(claims.UserID)
|
||||
pinned := int8(0)
|
||||
if payload.Pinned {
|
||||
pinned = 1
|
||||
}
|
||||
|
||||
note := &models.BackendNotebook{
|
||||
Tid: claims.TenantId,
|
||||
Title: title,
|
||||
Content: payload.Content,
|
||||
Pinned: pinned,
|
||||
UserID: &userID,
|
||||
UserName: &claims.Username,
|
||||
IsDeleted: 0,
|
||||
}
|
||||
|
||||
id, err := models.Orm.Insert(note)
|
||||
if err != nil {
|
||||
c.nbJsonErr(500, 500, "创建失败")
|
||||
return
|
||||
}
|
||||
|
||||
note.ID = uint64(id)
|
||||
c.nbOk(map[string]interface{}{
|
||||
"id": note.ID,
|
||||
"title": note.Title,
|
||||
"content": note.Content,
|
||||
"pinned": note.Pinned == 1,
|
||||
})
|
||||
}
|
||||
|
||||
// Update PUT /app/notebook/:id
|
||||
func (c *AppNotebookController) Update() {
|
||||
claims, err := c.appNotebookClaims()
|
||||
if err != nil {
|
||||
c.nbJsonErr(401, 401, "未登录或无权限")
|
||||
return
|
||||
}
|
||||
|
||||
id, err := strconv.ParseUint(c.Ctx.Input.Param(":id"), 10, 64)
|
||||
if err != nil || id == 0 {
|
||||
c.nbJsonErr(400, 400, "无效ID")
|
||||
return
|
||||
}
|
||||
|
||||
raw, err := io.ReadAll(c.Ctx.Request.Body)
|
||||
if err != nil {
|
||||
c.nbJsonErr(400, 400, "参数错误")
|
||||
return
|
||||
}
|
||||
var payload struct {
|
||||
Title string `json:"title"`
|
||||
Content string `json:"content"`
|
||||
Pinned *bool `json:"pinned"`
|
||||
}
|
||||
if err := json.Unmarshal(raw, &payload); err != nil {
|
||||
c.nbJsonErr(400, 400, "参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
var note models.BackendNotebook
|
||||
err = models.Orm.QueryTable(new(models.BackendNotebook)).
|
||||
Filter("id", id).
|
||||
Filter("is_deleted", 0).
|
||||
Filter("user_id", claims.UserID).
|
||||
One(¬e)
|
||||
if err != nil {
|
||||
c.nbJsonErr(404, 404, "笔记不存在")
|
||||
return
|
||||
}
|
||||
|
||||
updateFields := map[string]interface{}{
|
||||
"update_time": time.Now(),
|
||||
}
|
||||
|
||||
if payload.Title != "" {
|
||||
updateFields["title"] = strings.TrimSpace(payload.Title)
|
||||
}
|
||||
updateFields["content"] = payload.Content
|
||||
if payload.Pinned != nil {
|
||||
if *payload.Pinned {
|
||||
updateFields["pinned"] = int8(1)
|
||||
} else {
|
||||
updateFields["pinned"] = int8(0)
|
||||
}
|
||||
}
|
||||
|
||||
_, err = models.Orm.QueryTable(new(models.BackendNotebook)).
|
||||
Filter("id", id).
|
||||
Update(updateFields)
|
||||
if err != nil {
|
||||
c.nbJsonErr(500, 500, "更新失败")
|
||||
return
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
createdAt := note.CreateTime.Format("2006-01-02 15:04:05")
|
||||
|
||||
c.nbOk(map[string]interface{}{
|
||||
"id": note.ID,
|
||||
"title": updateFields["title"],
|
||||
"content": payload.Content,
|
||||
"pinned": payload.Pinned != nil && *payload.Pinned,
|
||||
"created_at": createdAt,
|
||||
"updated_at": now.Format("2006-01-02 15:04:05"),
|
||||
})
|
||||
}
|
||||
|
||||
// Delete DELETE /app/notebook/:id
|
||||
func (c *AppNotebookController) Delete() {
|
||||
claims, err := c.appNotebookClaims()
|
||||
if err != nil {
|
||||
c.nbJsonErr(401, 401, "未登录或无权限")
|
||||
return
|
||||
}
|
||||
|
||||
id, err := strconv.ParseUint(c.Ctx.Input.Param(":id"), 10, 64)
|
||||
if err != nil || id == 0 {
|
||||
c.nbJsonErr(400, 400, "无效ID")
|
||||
return
|
||||
}
|
||||
|
||||
var note models.BackendNotebook
|
||||
err = models.Orm.QueryTable(new(models.BackendNotebook)).
|
||||
Filter("id", id).
|
||||
Filter("is_deleted", 0).
|
||||
Filter("user_id", claims.UserID).
|
||||
One(¬e)
|
||||
if err != nil {
|
||||
c.nbJsonErr(404, 404, "笔记不存在")
|
||||
return
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
_, err = models.Orm.QueryTable(new(models.BackendNotebook)).
|
||||
Filter("id", id).
|
||||
Update(map[string]interface{}{
|
||||
"is_deleted": 1,
|
||||
"delete_time": now,
|
||||
})
|
||||
if err != nil {
|
||||
c.nbJsonErr(500, 500, "删除失败")
|
||||
return
|
||||
}
|
||||
|
||||
middleware.WriteDeleteLog(uint64(claims.UserID), claims.TenantId, "notebook", note.Title)
|
||||
c.nbOk(nil)
|
||||
}
|
||||
|
||||
// TogglePin POST /app/notebook/:id/togglePin
|
||||
func (c *AppNotebookController) TogglePin() {
|
||||
claims, err := c.appNotebookClaims()
|
||||
if err != nil {
|
||||
c.nbJsonErr(401, 401, "未登录或无权限")
|
||||
return
|
||||
}
|
||||
|
||||
id, err := strconv.ParseUint(c.Ctx.Input.Param(":id"), 10, 64)
|
||||
if err != nil || id == 0 {
|
||||
c.nbJsonErr(400, 400, "无效ID")
|
||||
return
|
||||
}
|
||||
|
||||
var note models.BackendNotebook
|
||||
err = models.Orm.QueryTable(new(models.BackendNotebook)).
|
||||
Filter("id", id).
|
||||
Filter("is_deleted", 0).
|
||||
Filter("user_id", claims.UserID).
|
||||
One(¬e)
|
||||
if err != nil {
|
||||
c.nbJsonErr(404, 404, "笔记不存在")
|
||||
return
|
||||
}
|
||||
|
||||
var newPinned int8
|
||||
if note.Pinned == 1 {
|
||||
newPinned = 0
|
||||
} else {
|
||||
newPinned = 1
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
_, err = models.Orm.QueryTable(new(models.BackendNotebook)).
|
||||
Filter("id", id).
|
||||
Update(map[string]interface{}{
|
||||
"pinned": newPinned,
|
||||
"update_time": now,
|
||||
})
|
||||
if err != nil {
|
||||
c.nbJsonErr(500, 500, "操作失败")
|
||||
return
|
||||
}
|
||||
|
||||
c.nbOk(map[string]interface{}{
|
||||
"pinned": newPinned == 1,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,586 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"server/middleware"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"server/models"
|
||||
"server/pkg/jwtutil"
|
||||
|
||||
beego "github.com/beego/beego/v2/server/web"
|
||||
)
|
||||
|
||||
type AppReminderController struct {
|
||||
beego.Controller
|
||||
}
|
||||
|
||||
func (c *AppReminderController) appClaims() (*jwtutil.Claims, error) {
|
||||
auth := c.Ctx.Request.Header.Get("Authorization")
|
||||
if auth == "" {
|
||||
return nil, fmt.Errorf("未登录")
|
||||
}
|
||||
parts := strings.SplitN(auth, " ", 2)
|
||||
if len(parts) != 2 || parts[0] != "Bearer" {
|
||||
return nil, fmt.Errorf("认证信息格式错误")
|
||||
}
|
||||
claims, err := jwtutil.ParseToken(parts[1])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("无效的token")
|
||||
}
|
||||
if claims.UserType != "backend" && claims.UserType != "app" && claims.UserType != "platform" {
|
||||
return nil, fmt.Errorf("无权访问")
|
||||
}
|
||||
return claims, nil
|
||||
}
|
||||
|
||||
func (c *AppReminderController) jsonErr(httpStatus, bizCode int, msg string) {
|
||||
c.Ctx.Output.SetStatus(httpStatus)
|
||||
c.Data["json"] = map[string]interface{}{"code": bizCode, "msg": msg}
|
||||
_ = c.ServeJSON()
|
||||
}
|
||||
|
||||
func (c *AppReminderController) ok(data interface{}) {
|
||||
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": data}
|
||||
_ = c.ServeJSON()
|
||||
}
|
||||
|
||||
func appGenerateToken() string {
|
||||
b := make([]byte, 16)
|
||||
_, _ = rand.Read(b)
|
||||
b[6] = (b[6] & 0x0f) | 0x40
|
||||
b[8] = (b[8] & 0x3f) | 0x80
|
||||
return fmt.Sprintf("%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:])
|
||||
}
|
||||
|
||||
type appSchedulePayload struct {
|
||||
Content string `json:"content"`
|
||||
ScheduleTime string `json:"schedule_time"`
|
||||
RemindChannels []string `json:"remind_channels"`
|
||||
AdvanceMinutes int `json:"advance_minutes"`
|
||||
RepeatIntervalMinutes int `json:"repeat_interval_minutes"`
|
||||
MaxSendCount int `json:"max_send_count"`
|
||||
}
|
||||
|
||||
// GetList GET /app/schedule/list
|
||||
func (c *AppReminderController) GetList() {
|
||||
claims, err := c.appClaims()
|
||||
if err != nil {
|
||||
c.jsonErr(401, 401, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
page, _ := c.GetInt("page", 1)
|
||||
pageSize, _ := c.GetInt("pageSize", 20)
|
||||
keyword := c.GetString("keyword", "")
|
||||
status := c.GetString("status", "")
|
||||
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize < 1 || pageSize > 100 {
|
||||
pageSize = 20
|
||||
}
|
||||
|
||||
qs := models.Orm.QueryTable(new(models.PlatformSchedule)).
|
||||
Filter("user_id", claims.UserID)
|
||||
|
||||
if keyword != "" {
|
||||
qs = qs.Filter("content__contains", keyword)
|
||||
}
|
||||
|
||||
total, _ := qs.Count()
|
||||
|
||||
var schedules []models.PlatformSchedule
|
||||
_, err = qs.OrderBy("-id").Limit(pageSize, (page-1)*pageSize).All(&schedules)
|
||||
if err != nil {
|
||||
c.jsonErr(500, 500, "查询失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
list := make([]map[string]interface{}, 0, len(schedules))
|
||||
for _, s := range schedules {
|
||||
var reminders []models.PlatformScheduleReminder
|
||||
_, _ = models.Orm.QueryTable(new(models.PlatformScheduleReminder)).
|
||||
Filter("schedule_id", s.ID).
|
||||
Filter("is_deleted", 0).
|
||||
All(&reminders)
|
||||
|
||||
channels := make([]string, 0, len(reminders))
|
||||
isFinished := true
|
||||
if len(reminders) == 0 {
|
||||
isFinished = false
|
||||
} else {
|
||||
for _, r := range reminders {
|
||||
channels = append(channels, r.RemindChannel)
|
||||
if r.RemindStatus != 2 {
|
||||
isFinished = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// status filter
|
||||
if status == "pending" && isFinished {
|
||||
continue
|
||||
}
|
||||
if status == "done" && !isFinished {
|
||||
continue
|
||||
}
|
||||
|
||||
item := map[string]interface{}{
|
||||
"id": s.ID,
|
||||
"content": s.Content,
|
||||
"schedule_time": s.ScheduleTime.Format("2006-01-02 15:04:05"),
|
||||
"remind_channels": channels,
|
||||
"is_finished": isFinished,
|
||||
"created_at": s.ScheduleTime.Format("2006-01-02 15:04:05"),
|
||||
"updated_at": s.ScheduleTime.Format("2006-01-02 15:04:05"),
|
||||
}
|
||||
if len(reminders) > 0 {
|
||||
first := reminders[0]
|
||||
item["advance_minutes"] = first.AdvanceMinutes
|
||||
if !first.CreateTime.IsZero() {
|
||||
item["created_at"] = first.CreateTime.Format("2006-01-02 15:04:05")
|
||||
}
|
||||
if !first.UpdateTime.IsZero() {
|
||||
item["updated_at"] = first.UpdateTime.Format("2006-01-02 15:04:05")
|
||||
}
|
||||
}
|
||||
list = append(list, item)
|
||||
}
|
||||
|
||||
c.ok(map[string]interface{}{
|
||||
"list": list,
|
||||
"total": total,
|
||||
"page": page,
|
||||
"pageSize": pageSize,
|
||||
})
|
||||
}
|
||||
|
||||
// GetDetail GET /app/schedule/:id
|
||||
func (c *AppReminderController) GetDetail() {
|
||||
claims, err := c.appClaims()
|
||||
if err != nil {
|
||||
c.jsonErr(401, 401, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
idStr := c.Ctx.Input.Param(":id")
|
||||
id, _ := strconv.ParseUint(idStr, 10, 64)
|
||||
if id == 0 {
|
||||
c.jsonErr(400, 400, "无效的ID")
|
||||
return
|
||||
}
|
||||
|
||||
var schedule models.PlatformSchedule
|
||||
err = models.Orm.QueryTable(new(models.PlatformSchedule)).
|
||||
Filter("id", id).
|
||||
Filter("user_id", claims.UserID).
|
||||
One(&schedule)
|
||||
if err != nil {
|
||||
c.jsonErr(404, 404, "日程未找到")
|
||||
return
|
||||
}
|
||||
|
||||
var reminders []models.PlatformScheduleReminder
|
||||
_, _ = models.Orm.QueryTable(new(models.PlatformScheduleReminder)).
|
||||
Filter("schedule_id", schedule.ID).
|
||||
Filter("is_deleted", 0).
|
||||
All(&reminders)
|
||||
|
||||
channels := make([]string, 0, len(reminders))
|
||||
var first models.PlatformScheduleReminder
|
||||
for _, r := range reminders {
|
||||
channels = append(channels, r.RemindChannel)
|
||||
first = r
|
||||
}
|
||||
|
||||
isFinished := true
|
||||
if len(reminders) == 0 {
|
||||
isFinished = false
|
||||
} else {
|
||||
for _, r := range reminders {
|
||||
if r.RemindStatus != 2 {
|
||||
isFinished = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data := map[string]interface{}{
|
||||
"id": schedule.ID,
|
||||
"content": schedule.Content,
|
||||
"schedule_time": schedule.ScheduleTime.Format("2006-01-02 15:04:05"),
|
||||
"remind_channels": channels,
|
||||
"is_finished": isFinished,
|
||||
}
|
||||
if first.ID > 0 {
|
||||
data["advance_minutes"] = first.AdvanceMinutes
|
||||
if !first.CreateTime.IsZero() {
|
||||
data["created_at"] = first.CreateTime.Format("2006-01-02 15:04:05")
|
||||
}
|
||||
if !first.UpdateTime.IsZero() {
|
||||
data["updated_at"] = first.UpdateTime.Format("2006-01-02 15:04:05")
|
||||
}
|
||||
}
|
||||
|
||||
c.ok(data)
|
||||
}
|
||||
|
||||
// Create POST /app/schedule
|
||||
func (c *AppReminderController) Create() {
|
||||
claims, err := c.appClaims()
|
||||
if err != nil {
|
||||
c.jsonErr(401, 401, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
raw, err := io.ReadAll(c.Ctx.Request.Body)
|
||||
if err != nil {
|
||||
c.jsonErr(400, 400, "参数错误")
|
||||
return
|
||||
}
|
||||
var p appSchedulePayload
|
||||
if err := json.Unmarshal(raw, &p); err != nil {
|
||||
c.jsonErr(400, 400, "参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
if strings.TrimSpace(p.Content) == "" {
|
||||
c.jsonErr(400, 400, "日程内容不能为空")
|
||||
return
|
||||
}
|
||||
if strings.TrimSpace(p.ScheduleTime) == "" {
|
||||
c.jsonErr(400, 400, "日程发生时间不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
schedTime, err := time.ParseInLocation("2006-01-02 15:04:05", p.ScheduleTime, time.Local)
|
||||
if err != nil {
|
||||
c.jsonErr(400, 400, "日程时间格式不合法,支持 YYYY-MM-DD HH:mm:ss")
|
||||
return
|
||||
}
|
||||
|
||||
content := strings.TrimSpace(p.Content)
|
||||
title := strings.SplitN(content, "\n", 2)[0]
|
||||
if len(title) > 60 {
|
||||
title = title[:60]
|
||||
}
|
||||
if title == "" {
|
||||
title = "日程提醒"
|
||||
}
|
||||
|
||||
schedule := models.PlatformSchedule{
|
||||
Title: title,
|
||||
Content: content,
|
||||
ScheduleTime: schedTime,
|
||||
UserID: uint64(claims.UserID),
|
||||
}
|
||||
schedID, err := models.Orm.Insert(&schedule)
|
||||
if err != nil {
|
||||
c.jsonErr(500, 500, "保存日程失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
for _, ch := range p.RemindChannels {
|
||||
ch = strings.ToUpper(strings.TrimSpace(ch))
|
||||
if ch != "SMS" && ch != "EMAIL" && ch != "BARK" && ch != "SITE_MSG" && ch != "APP" {
|
||||
continue
|
||||
}
|
||||
if ch == "APP" {
|
||||
ch = "SITE_MSG"
|
||||
}
|
||||
|
||||
firstSendTime := schedTime.Add(-time.Duration(p.AdvanceMinutes) * time.Minute)
|
||||
|
||||
reminder := models.PlatformScheduleReminder{
|
||||
ScheduleID: uint64(schedID),
|
||||
RemindChannel: ch,
|
||||
AdvanceMinutes: p.AdvanceMinutes,
|
||||
NextRemindTime: firstSendTime,
|
||||
ReceiverUserID: uint64(claims.UserID),
|
||||
RemindStatus: 0,
|
||||
CreateTime: time.Now(),
|
||||
UpdateTime: time.Now(),
|
||||
}
|
||||
|
||||
if ch == "EMAIL" || ch == "BARK" {
|
||||
token := appGenerateToken()
|
||||
reminder.AckToken = &token
|
||||
reminder.RepeatIntervalMinutes = p.RepeatIntervalMinutes
|
||||
reminder.MaxSendCount = p.MaxSendCount
|
||||
if reminder.MaxSendCount <= 0 {
|
||||
reminder.MaxSendCount = 1
|
||||
}
|
||||
} else {
|
||||
reminder.RepeatIntervalMinutes = 0
|
||||
reminder.MaxSendCount = 1
|
||||
}
|
||||
|
||||
_, err = models.Orm.Insert(&reminder)
|
||||
if err != nil {
|
||||
c.jsonErr(500, 500, "创建提醒失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
c.ok(map[string]interface{}{"id": schedID})
|
||||
}
|
||||
|
||||
// Update PUT /app/schedule/:id
|
||||
func (c *AppReminderController) Update() {
|
||||
claims, err := c.appClaims()
|
||||
if err != nil {
|
||||
c.jsonErr(401, 401, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
idStr := c.Ctx.Input.Param(":id")
|
||||
id, _ := strconv.ParseUint(idStr, 10, 64)
|
||||
if id == 0 {
|
||||
c.jsonErr(400, 400, "无效的ID")
|
||||
return
|
||||
}
|
||||
|
||||
raw, err := io.ReadAll(c.Ctx.Request.Body)
|
||||
if err != nil {
|
||||
c.jsonErr(400, 400, "参数错误")
|
||||
return
|
||||
}
|
||||
var p appSchedulePayload
|
||||
if err := json.Unmarshal(raw, &p); err != nil {
|
||||
c.jsonErr(400, 400, "参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
schedTime, err := time.ParseInLocation("2006-01-02 15:04:05", p.ScheduleTime, time.Local)
|
||||
if err != nil {
|
||||
c.jsonErr(400, 400, "日程时间格式不合法")
|
||||
return
|
||||
}
|
||||
|
||||
var schedule models.PlatformSchedule
|
||||
err = models.Orm.QueryTable(new(models.PlatformSchedule)).
|
||||
Filter("id", id).
|
||||
Filter("user_id", claims.UserID).
|
||||
One(&schedule)
|
||||
if err != nil {
|
||||
c.jsonErr(404, 404, "日程未找到")
|
||||
return
|
||||
}
|
||||
|
||||
var reminders []models.PlatformScheduleReminder
|
||||
_, _ = models.Orm.QueryTable(new(models.PlatformScheduleReminder)).
|
||||
Filter("schedule_id", id).
|
||||
Filter("is_deleted", 0).
|
||||
All(&reminders)
|
||||
isFinished := true
|
||||
if len(reminders) == 0 {
|
||||
isFinished = false
|
||||
} else {
|
||||
for _, r := range reminders {
|
||||
if r.RemindStatus != 2 {
|
||||
isFinished = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if isFinished {
|
||||
c.jsonErr(400, 400, "该日程提醒已全部结束,无法编辑")
|
||||
return
|
||||
}
|
||||
|
||||
content := strings.TrimSpace(p.Content)
|
||||
if content == "" {
|
||||
content = schedule.Content
|
||||
}
|
||||
title := strings.SplitN(content, "\n", 2)[0]
|
||||
if len(title) > 60 {
|
||||
title = title[:60]
|
||||
}
|
||||
|
||||
schedule.Title = title
|
||||
schedule.Content = content
|
||||
schedule.ScheduleTime = schedTime
|
||||
_, err = models.Orm.Update(&schedule, "Title", "Content", "ScheduleTime")
|
||||
if err != nil {
|
||||
c.jsonErr(500, 500, "更新失败")
|
||||
return
|
||||
}
|
||||
|
||||
// 软删除旧提醒,重建
|
||||
_, _ = models.Orm.QueryTable(new(models.PlatformScheduleReminder)).
|
||||
Filter("schedule_id", id).
|
||||
Update(map[string]interface{}{
|
||||
"IsDeleted": 1,
|
||||
"UpdateTime": time.Now(),
|
||||
})
|
||||
|
||||
for _, ch := range p.RemindChannels {
|
||||
ch = strings.ToUpper(strings.TrimSpace(ch))
|
||||
if ch != "SMS" && ch != "EMAIL" && ch != "BARK" && ch != "SITE_MSG" && ch != "APP" {
|
||||
continue
|
||||
}
|
||||
if ch == "APP" {
|
||||
ch = "SITE_MSG"
|
||||
}
|
||||
|
||||
firstSendTime := schedTime.Add(-time.Duration(p.AdvanceMinutes) * time.Minute)
|
||||
|
||||
reminder := models.PlatformScheduleReminder{
|
||||
ScheduleID: id,
|
||||
RemindChannel: ch,
|
||||
AdvanceMinutes: p.AdvanceMinutes,
|
||||
NextRemindTime: firstSendTime,
|
||||
ReceiverUserID: schedule.UserID,
|
||||
RemindStatus: 0,
|
||||
CreateTime: time.Now(),
|
||||
UpdateTime: time.Now(),
|
||||
}
|
||||
|
||||
if ch == "EMAIL" || ch == "BARK" {
|
||||
token := appGenerateToken()
|
||||
reminder.AckToken = &token
|
||||
reminder.RepeatIntervalMinutes = p.RepeatIntervalMinutes
|
||||
reminder.MaxSendCount = p.MaxSendCount
|
||||
if reminder.MaxSendCount <= 0 {
|
||||
reminder.MaxSendCount = 1
|
||||
}
|
||||
} else {
|
||||
reminder.RepeatIntervalMinutes = 0
|
||||
reminder.MaxSendCount = 1
|
||||
}
|
||||
|
||||
_, err = models.Orm.Insert(&reminder)
|
||||
if err != nil {
|
||||
c.jsonErr(500, 500, "重新创建提醒失败")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
c.ok(nil)
|
||||
}
|
||||
|
||||
// Delete DELETE /app/schedule/:id
|
||||
func (c *AppReminderController) Delete() {
|
||||
claims, err := c.appClaims()
|
||||
if err != nil {
|
||||
c.jsonErr(401, 401, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
idStr := c.Ctx.Input.Param(":id")
|
||||
id, _ := strconv.ParseUint(idStr, 10, 64)
|
||||
if id == 0 {
|
||||
c.jsonErr(400, 400, "无效的ID")
|
||||
return
|
||||
}
|
||||
|
||||
var schedule models.PlatformSchedule
|
||||
err = models.Orm.QueryTable(new(models.PlatformSchedule)).
|
||||
Filter("id", id).
|
||||
Filter("user_id", claims.UserID).
|
||||
One(&schedule)
|
||||
if err != nil {
|
||||
c.jsonErr(404, 404, "日程未找到")
|
||||
return
|
||||
}
|
||||
|
||||
var reminders []models.PlatformScheduleReminder
|
||||
_, _ = models.Orm.QueryTable(new(models.PlatformScheduleReminder)).
|
||||
Filter("schedule_id", id).
|
||||
Filter("is_deleted", 0).
|
||||
All(&reminders)
|
||||
isFinished := true
|
||||
if len(reminders) == 0 {
|
||||
isFinished = false
|
||||
} else {
|
||||
for _, r := range reminders {
|
||||
if r.RemindStatus != 2 {
|
||||
isFinished = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if isFinished {
|
||||
c.jsonErr(400, 400, "该日程提醒已全部结束,无法删除")
|
||||
return
|
||||
}
|
||||
|
||||
_, err = models.Orm.QueryTable(new(models.PlatformSchedule)).Filter("id", id).Delete()
|
||||
if err == nil {
|
||||
_, _ = models.Orm.QueryTable(new(models.PlatformScheduleReminder)).
|
||||
Filter("schedule_id", id).
|
||||
Update(map[string]interface{}{
|
||||
"IsDeleted": 1,
|
||||
"UpdateTime": time.Now(),
|
||||
})
|
||||
}
|
||||
|
||||
middleware.WriteDeleteLog(uint64(claims.UserID), claims.TenantId, "schedule", schedule.Content)
|
||||
c.ok(nil)
|
||||
}
|
||||
|
||||
// ToggleComplete POST /app/schedule/:id/toggle
|
||||
func (c *AppReminderController) ToggleComplete() {
|
||||
claims, err := c.appClaims()
|
||||
if err != nil {
|
||||
c.jsonErr(401, 401, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
idStr := c.Ctx.Input.Param(":id")
|
||||
id, _ := strconv.ParseUint(idStr, 10, 64)
|
||||
if id == 0 {
|
||||
c.jsonErr(400, 400, "无效的ID")
|
||||
return
|
||||
}
|
||||
|
||||
var schedule models.PlatformSchedule
|
||||
err = models.Orm.QueryTable(new(models.PlatformSchedule)).
|
||||
Filter("id", id).
|
||||
Filter("user_id", claims.UserID).
|
||||
One(&schedule)
|
||||
if err != nil {
|
||||
c.jsonErr(404, 404, "日程未找到")
|
||||
return
|
||||
}
|
||||
|
||||
var reminders []models.PlatformScheduleReminder
|
||||
_, _ = models.Orm.QueryTable(new(models.PlatformScheduleReminder)).
|
||||
Filter("schedule_id", id).
|
||||
Filter("is_deleted", 0).
|
||||
All(&reminders)
|
||||
|
||||
// Toggle: if all finished -> set to pending; else -> set to finished
|
||||
allFinished := len(reminders) > 0
|
||||
for _, r := range reminders {
|
||||
if r.RemindStatus != 2 {
|
||||
allFinished = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
var newStatus int8
|
||||
if allFinished {
|
||||
newStatus = 0 // reopen
|
||||
} else {
|
||||
newStatus = 2 // mark done
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
for _, r := range reminders {
|
||||
r.RemindStatus = newStatus
|
||||
r.UpdateTime = now
|
||||
models.Orm.Update(&r, "RemindStatus", "UpdateTime")
|
||||
}
|
||||
|
||||
c.ok(map[string]interface{}{
|
||||
"is_finished": newStatus == 2,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,274 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"server/models"
|
||||
"server/pkg/jwtutil"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/beego/beego/v2/client/orm"
|
||||
beego "github.com/beego/beego/v2/server/web"
|
||||
)
|
||||
|
||||
type BackendNotebookController struct {
|
||||
beego.Controller
|
||||
}
|
||||
|
||||
func (c *BackendNotebookController) backendNotebookClaims() (*jwtutil.Claims, error) {
|
||||
auth := c.Ctx.Request.Header.Get("Authorization")
|
||||
if auth == "" {
|
||||
return nil, orm.ErrNoRows
|
||||
}
|
||||
parts := strings.SplitN(auth, " ", 2)
|
||||
if len(parts) != 2 || parts[0] != "Bearer" {
|
||||
return nil, orm.ErrNoRows
|
||||
}
|
||||
claims, err := jwtutil.ParseToken(parts[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if claims.UserType != "backend" {
|
||||
return nil, orm.ErrNoRows
|
||||
}
|
||||
return claims, nil
|
||||
}
|
||||
|
||||
func (c *BackendNotebookController) nbJsonErr(httpStatus, bizCode int, msg string) {
|
||||
c.Ctx.Output.SetStatus(httpStatus)
|
||||
c.Data["json"] = map[string]interface{}{"code": bizCode, "msg": msg}
|
||||
_ = c.ServeJSON()
|
||||
}
|
||||
|
||||
func (c *BackendNotebookController) nbOk(data interface{}) {
|
||||
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": data}
|
||||
_ = c.ServeJSON()
|
||||
}
|
||||
|
||||
// List GET /backend/notebook/list
|
||||
func (c *BackendNotebookController) List() {
|
||||
claims, err := c.backendNotebookClaims()
|
||||
if err != nil {
|
||||
c.nbJsonErr(401, 401, "未登录或无权限")
|
||||
return
|
||||
}
|
||||
|
||||
page, _ := c.GetInt("page", 1)
|
||||
pageSize, _ := c.GetInt("pageSize", 20)
|
||||
keyword := strings.TrimSpace(c.GetString("keyword"))
|
||||
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize < 1 || pageSize > 100 {
|
||||
pageSize = 20
|
||||
}
|
||||
|
||||
qs := models.Orm.QueryTable(new(models.BackendNotebook)).
|
||||
Filter("is_deleted", 0).
|
||||
Filter("tid", claims.TenantId).
|
||||
Filter("user_id", claims.UserID)
|
||||
|
||||
if keyword != "" {
|
||||
qs = qs.Filter("title__icontains", keyword)
|
||||
}
|
||||
|
||||
total, _ := qs.Count()
|
||||
|
||||
var list []models.BackendNotebook
|
||||
_, err = qs.OrderBy("-update_time", "-create_time").
|
||||
Limit(pageSize).Offset((page - 1) * pageSize).
|
||||
All(&list)
|
||||
if err != nil && err != orm.ErrNoRows {
|
||||
c.nbJsonErr(500, 500, "查询失败")
|
||||
return
|
||||
}
|
||||
if list == nil {
|
||||
list = []models.BackendNotebook{}
|
||||
}
|
||||
|
||||
c.nbOk(map[string]interface{}{"list": list, "total": total})
|
||||
}
|
||||
|
||||
// Detail GET /backend/notebook/detail/:id
|
||||
func (c *BackendNotebookController) Detail() {
|
||||
claims, err := c.backendNotebookClaims()
|
||||
if err != nil {
|
||||
c.nbJsonErr(401, 401, "未登录或无权限")
|
||||
return
|
||||
}
|
||||
|
||||
id, err := strconv.ParseUint(c.Ctx.Input.Param(":id"), 10, 64)
|
||||
if err != nil || id == 0 {
|
||||
c.nbJsonErr(400, 400, "无效ID")
|
||||
return
|
||||
}
|
||||
|
||||
var note models.BackendNotebook
|
||||
err = models.Orm.QueryTable(new(models.BackendNotebook)).
|
||||
Filter("id", id).
|
||||
Filter("is_deleted", 0).
|
||||
Filter("tid", claims.TenantId).
|
||||
Filter("user_id", claims.UserID).
|
||||
One(¬e)
|
||||
if err != nil {
|
||||
c.nbJsonErr(404, 404, "笔记不存在")
|
||||
return
|
||||
}
|
||||
|
||||
c.nbOk(note)
|
||||
}
|
||||
|
||||
// Create POST /backend/notebook/create
|
||||
func (c *BackendNotebookController) Create() {
|
||||
claims, err := c.backendNotebookClaims()
|
||||
if err != nil {
|
||||
c.nbJsonErr(401, 401, "未登录或无权限")
|
||||
return
|
||||
}
|
||||
|
||||
raw, err := io.ReadAll(c.Ctx.Request.Body)
|
||||
if err != nil {
|
||||
c.nbJsonErr(400, 400, "参数错误")
|
||||
return
|
||||
}
|
||||
var payload struct {
|
||||
Title string `json:"title"`
|
||||
Content string `json:"content"`
|
||||
}
|
||||
if err := json.Unmarshal(raw, &payload); err != nil {
|
||||
c.nbJsonErr(400, 400, "参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
payload.Title = strings.TrimSpace(payload.Title)
|
||||
if payload.Title == "" {
|
||||
payload.Title = "无标题"
|
||||
}
|
||||
|
||||
userID := uint64(claims.UserID)
|
||||
note := &models.BackendNotebook{
|
||||
Tid: claims.TenantId,
|
||||
Title: payload.Title,
|
||||
Content: payload.Content,
|
||||
UserID: &userID,
|
||||
UserName: &claims.Username,
|
||||
IsDeleted: 0,
|
||||
}
|
||||
|
||||
id, err := models.Orm.Insert(note)
|
||||
if err != nil {
|
||||
c.nbJsonErr(500, 500, "创建失败")
|
||||
return
|
||||
}
|
||||
|
||||
note.ID = uint64(id)
|
||||
c.nbOk(note)
|
||||
}
|
||||
|
||||
// Update POST /backend/notebook/update/:id
|
||||
func (c *BackendNotebookController) Update() {
|
||||
claims, err := c.backendNotebookClaims()
|
||||
if err != nil {
|
||||
c.nbJsonErr(401, 401, "未登录或无权限")
|
||||
return
|
||||
}
|
||||
|
||||
id, err := strconv.ParseUint(c.Ctx.Input.Param(":id"), 10, 64)
|
||||
if err != nil || id == 0 {
|
||||
c.nbJsonErr(400, 400, "无效ID")
|
||||
return
|
||||
}
|
||||
|
||||
raw, err := io.ReadAll(c.Ctx.Request.Body)
|
||||
if err != nil {
|
||||
c.nbJsonErr(400, 400, "参数错误")
|
||||
return
|
||||
}
|
||||
var payload struct {
|
||||
Title string `json:"title"`
|
||||
Content string `json:"content"`
|
||||
}
|
||||
if err := json.Unmarshal(raw, &payload); err != nil {
|
||||
c.nbJsonErr(400, 400, "参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
payload.Title = strings.TrimSpace(payload.Title)
|
||||
if payload.Title == "" {
|
||||
payload.Title = "无标题"
|
||||
}
|
||||
|
||||
var note models.BackendNotebook
|
||||
err = models.Orm.QueryTable(new(models.BackendNotebook)).
|
||||
Filter("id", id).
|
||||
Filter("is_deleted", 0).
|
||||
Filter("tid", claims.TenantId).
|
||||
Filter("user_id", claims.UserID).
|
||||
One(¬e)
|
||||
if err != nil {
|
||||
c.nbJsonErr(404, 404, "笔记不存在")
|
||||
return
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
_, err = models.Orm.QueryTable(new(models.BackendNotebook)).
|
||||
Filter("id", id).
|
||||
Update(map[string]interface{}{
|
||||
"title": payload.Title,
|
||||
"content": payload.Content,
|
||||
"update_time": now,
|
||||
})
|
||||
if err != nil {
|
||||
c.nbJsonErr(500, 500, "更新失败")
|
||||
return
|
||||
}
|
||||
|
||||
note.Title = payload.Title
|
||||
note.Content = payload.Content
|
||||
note.UpdateTime = &now
|
||||
c.nbOk(note)
|
||||
}
|
||||
|
||||
// Delete DELETE /backend/notebook/delete/:id
|
||||
func (c *BackendNotebookController) Delete() {
|
||||
claims, err := c.backendNotebookClaims()
|
||||
if err != nil {
|
||||
c.nbJsonErr(401, 401, "未登录或无权限")
|
||||
return
|
||||
}
|
||||
|
||||
id, err := strconv.ParseUint(c.Ctx.Input.Param(":id"), 10, 64)
|
||||
if err != nil || id == 0 {
|
||||
c.nbJsonErr(400, 400, "无效ID")
|
||||
return
|
||||
}
|
||||
|
||||
var note models.BackendNotebook
|
||||
err = models.Orm.QueryTable(new(models.BackendNotebook)).
|
||||
Filter("id", id).
|
||||
Filter("is_deleted", 0).
|
||||
Filter("tid", claims.TenantId).
|
||||
Filter("user_id", claims.UserID).
|
||||
One(¬e)
|
||||
if err != nil {
|
||||
c.nbJsonErr(404, 404, "笔记不存在")
|
||||
return
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
_, err = models.Orm.QueryTable(new(models.BackendNotebook)).
|
||||
Filter("id", id).
|
||||
Update(map[string]interface{}{
|
||||
"is_deleted": 1,
|
||||
"delete_time": now,
|
||||
})
|
||||
if err != nil {
|
||||
c.nbJsonErr(500, 500, "删除失败")
|
||||
return
|
||||
}
|
||||
|
||||
c.nbOk(nil)
|
||||
}
|
||||
@@ -0,0 +1,724 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"server/models"
|
||||
"server/pkg/jwtutil"
|
||||
"server/services"
|
||||
|
||||
beego "github.com/beego/beego/v2/server/web"
|
||||
)
|
||||
|
||||
type BackendReminderController struct {
|
||||
beego.Controller
|
||||
}
|
||||
|
||||
func (c *BackendReminderController) backendReminderClaims() (*jwtutil.Claims, error) {
|
||||
auth := c.Ctx.Request.Header.Get("Authorization")
|
||||
if auth == "" {
|
||||
return nil, fmt.Errorf("未登录")
|
||||
}
|
||||
parts := strings.SplitN(auth, " ", 2)
|
||||
if len(parts) != 2 || parts[0] != "Bearer" {
|
||||
return nil, fmt.Errorf("认证信息格式错误")
|
||||
}
|
||||
claims, err := jwtutil.ParseToken(parts[1])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("无效的token")
|
||||
}
|
||||
if claims.UserType != "backend" {
|
||||
return nil, fmt.Errorf("无权访问")
|
||||
}
|
||||
return claims, nil
|
||||
}
|
||||
|
||||
func (c *BackendReminderController) brJsonErr(httpStatus, bizCode int, msg string) {
|
||||
c.Ctx.Output.SetStatus(httpStatus)
|
||||
c.Data["json"] = map[string]interface{}{"code": bizCode, "msg": msg}
|
||||
_ = c.ServeJSON()
|
||||
}
|
||||
|
||||
func (c *BackendReminderController) brOk(data interface{}) {
|
||||
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": data}
|
||||
_ = c.ServeJSON()
|
||||
}
|
||||
|
||||
func backendGenerateToken() string {
|
||||
b := make([]byte, 16)
|
||||
_, _ = rand.Read(b)
|
||||
b[6] = (b[6] & 0x0f) | 0x40
|
||||
b[8] = (b[8] & 0x3f) | 0x80
|
||||
return fmt.Sprintf("%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:])
|
||||
}
|
||||
|
||||
type backendReminderFormPayload struct {
|
||||
Title string `json:"title"`
|
||||
Content string `json:"content"`
|
||||
ScheduleTime string `json:"schedule_time"`
|
||||
RemindChannels []string `json:"remind_channels"`
|
||||
AdvanceMinutes int `json:"advance_minutes"`
|
||||
RepeatIntervalMinutes int `json:"repeat_interval_minutes"`
|
||||
MaxSendCount int `json:"max_send_count"`
|
||||
ReceiverUserID uint64 `json:"receiver_user_id"`
|
||||
ReceiverTargets map[string]string `json:"receiver_targets"`
|
||||
}
|
||||
|
||||
// GetReminderList GET /backend/reminder/list
|
||||
func (c *BackendReminderController) GetReminderList() {
|
||||
claims, err := c.backendReminderClaims()
|
||||
if err != nil {
|
||||
c.brJsonErr(401, 401, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
page, _ := c.GetInt("page", 1)
|
||||
pageSize, _ := c.GetInt("pageSize", 20)
|
||||
keyword := strings.TrimSpace(c.GetString("keyword"))
|
||||
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize < 1 || pageSize > 100 {
|
||||
pageSize = 20
|
||||
}
|
||||
|
||||
qs := models.Orm.QueryTable(new(models.BackendSchedule)).
|
||||
Filter("tid", claims.TenantId)
|
||||
|
||||
if keyword != "" {
|
||||
qs = qs.Filter("content__contains", keyword)
|
||||
}
|
||||
|
||||
total, _ := qs.Count()
|
||||
|
||||
var schedules []models.BackendSchedule
|
||||
_, err = qs.OrderBy("-id").Limit(pageSize, (page-1)*pageSize).All(&schedules)
|
||||
if err != nil {
|
||||
c.brJsonErr(500, 500, "查询失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
list := make([]map[string]interface{}, 0, len(schedules))
|
||||
for _, s := range schedules {
|
||||
var reminders []models.BackendScheduleReminder
|
||||
_, _ = models.Orm.QueryTable(new(models.BackendScheduleReminder)).
|
||||
Filter("schedule_id", s.ID).
|
||||
Filter("is_deleted", 0).
|
||||
All(&reminders)
|
||||
|
||||
channels := make([]string, 0, len(reminders))
|
||||
isFinished := true
|
||||
if len(reminders) == 0 {
|
||||
isFinished = false
|
||||
} else {
|
||||
for _, r := range reminders {
|
||||
channels = append(channels, r.RemindChannel)
|
||||
if r.RemindStatus != 2 {
|
||||
isFinished = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
item := map[string]interface{}{
|
||||
"id": s.ID,
|
||||
"title": s.Title,
|
||||
"content": s.Content,
|
||||
"schedule_time": s.ScheduleTime.Format("2006-01-02 15:04:05"),
|
||||
"remind_channels": channels,
|
||||
"user_id": s.UserID,
|
||||
"is_finished": isFinished,
|
||||
}
|
||||
if len(reminders) > 0 {
|
||||
first := reminders[0]
|
||||
item["advance_minutes"] = first.AdvanceMinutes
|
||||
item["repeat_interval_minutes"] = first.RepeatIntervalMinutes
|
||||
item["max_send_count"] = first.MaxSendCount
|
||||
item["receiver_user_id"] = first.ReceiverUserID
|
||||
}
|
||||
list = append(list, item)
|
||||
}
|
||||
|
||||
c.brOk(map[string]interface{}{
|
||||
"list": list,
|
||||
"total": total,
|
||||
"page": page,
|
||||
"pageSize": pageSize,
|
||||
})
|
||||
}
|
||||
|
||||
// GetReminderDetail GET /backend/reminder/:id
|
||||
func (c *BackendReminderController) GetReminderDetail() {
|
||||
claims, err := c.backendReminderClaims()
|
||||
if err != nil {
|
||||
c.brJsonErr(401, 401, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
idStr := c.Ctx.Input.Param(":id")
|
||||
id, _ := strconv.ParseUint(idStr, 10, 64)
|
||||
if id == 0 {
|
||||
c.brJsonErr(400, 400, "无效的ID")
|
||||
return
|
||||
}
|
||||
|
||||
var schedule models.BackendSchedule
|
||||
err = models.Orm.QueryTable(new(models.BackendSchedule)).
|
||||
Filter("id", id).
|
||||
Filter("tid", claims.TenantId).
|
||||
One(&schedule)
|
||||
if err != nil {
|
||||
c.brJsonErr(404, 404, "日程未找到")
|
||||
return
|
||||
}
|
||||
|
||||
var reminders []models.BackendScheduleReminder
|
||||
_, _ = models.Orm.QueryTable(new(models.BackendScheduleReminder)).
|
||||
Filter("schedule_id", schedule.ID).
|
||||
Filter("is_deleted", 0).
|
||||
All(&reminders)
|
||||
|
||||
channels := make([]string, 0, len(reminders))
|
||||
targets := make(map[string]string)
|
||||
var first models.BackendScheduleReminder
|
||||
|
||||
for _, r := range reminders {
|
||||
channels = append(channels, r.RemindChannel)
|
||||
if r.ReceiverTarget != nil {
|
||||
targets[r.RemindChannel] = *r.ReceiverTarget
|
||||
}
|
||||
first = r
|
||||
}
|
||||
|
||||
isFinished := true
|
||||
if len(reminders) == 0 {
|
||||
isFinished = false
|
||||
} else {
|
||||
for _, r := range reminders {
|
||||
if r.RemindStatus != 2 {
|
||||
isFinished = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data := map[string]interface{}{
|
||||
"id": schedule.ID,
|
||||
"title": schedule.Title,
|
||||
"content": schedule.Content,
|
||||
"schedule_time": schedule.ScheduleTime.Format("2006-01-02 15:04:05"),
|
||||
"remind_channels": channels,
|
||||
"receiver_targets": targets,
|
||||
"is_finished": isFinished,
|
||||
}
|
||||
if first.ID > 0 {
|
||||
data["advance_minutes"] = first.AdvanceMinutes
|
||||
data["repeat_interval_minutes"] = first.RepeatIntervalMinutes
|
||||
data["max_send_count"] = first.MaxSendCount
|
||||
data["receiver_user_id"] = first.ReceiverUserID
|
||||
}
|
||||
|
||||
c.brOk(data)
|
||||
}
|
||||
|
||||
// CreateReminder POST /backend/reminder
|
||||
func (c *BackendReminderController) CreateReminder() {
|
||||
claims, err := c.backendReminderClaims()
|
||||
if err != nil {
|
||||
c.brJsonErr(401, 401, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
raw, err := io.ReadAll(c.Ctx.Request.Body)
|
||||
if err != nil {
|
||||
c.brJsonErr(400, 400, "参数错误")
|
||||
return
|
||||
}
|
||||
var p backendReminderFormPayload
|
||||
if err := json.Unmarshal(raw, &p); err != nil {
|
||||
c.brJsonErr(400, 400, "参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
if strings.TrimSpace(p.ScheduleTime) == "" {
|
||||
c.brJsonErr(400, 400, "日程发生时间不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
schedTime, err := time.ParseInLocation("2006-01-02 15:04:05", p.ScheduleTime, time.Local)
|
||||
if err != nil {
|
||||
c.brJsonErr(400, 400, "日程时间格式不合法,支持 YYYY-MM-DD HH:mm:ss")
|
||||
return
|
||||
}
|
||||
|
||||
content := strings.TrimSpace(p.Content)
|
||||
title := strings.SplitN(content, "\n", 2)[0]
|
||||
if len(title) > 60 {
|
||||
title = title[:60]
|
||||
}
|
||||
if title == "" {
|
||||
title = "日程提醒"
|
||||
}
|
||||
|
||||
schedule := models.BackendSchedule{
|
||||
Tid: claims.TenantId,
|
||||
Title: title,
|
||||
Content: content,
|
||||
ScheduleTime: schedTime,
|
||||
UserID: uint64(claims.UserID),
|
||||
}
|
||||
schedID, err := models.Orm.Insert(&schedule)
|
||||
if err != nil {
|
||||
c.brJsonErr(500, 500, "保存日程失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
for _, ch := range p.RemindChannels {
|
||||
ch = strings.ToUpper(strings.TrimSpace(ch))
|
||||
if ch != "SMS" && ch != "EMAIL" && ch != "BARK" && ch != "SITE_MSG" {
|
||||
continue
|
||||
}
|
||||
|
||||
targetVal := p.ReceiverTargets[ch]
|
||||
var target *string
|
||||
if targetVal != "" {
|
||||
target = &targetVal
|
||||
}
|
||||
|
||||
firstSendTime := schedTime.Add(-time.Duration(p.AdvanceMinutes) * time.Minute)
|
||||
|
||||
reminder := models.BackendScheduleReminder{
|
||||
Tid: claims.TenantId,
|
||||
ScheduleID: uint64(schedID),
|
||||
RemindChannel: ch,
|
||||
AdvanceMinutes: p.AdvanceMinutes,
|
||||
NextRemindTime: firstSendTime,
|
||||
ReceiverUserID: uint64(claims.UserID),
|
||||
ReceiverTarget: target,
|
||||
RemindStatus: 0,
|
||||
CreateTime: time.Now(),
|
||||
UpdateTime: time.Now(),
|
||||
}
|
||||
|
||||
if ch == "EMAIL" || ch == "BARK" {
|
||||
token := backendGenerateToken()
|
||||
reminder.AckToken = &token
|
||||
reminder.RepeatIntervalMinutes = p.RepeatIntervalMinutes
|
||||
reminder.MaxSendCount = p.MaxSendCount
|
||||
if reminder.MaxSendCount <= 0 {
|
||||
reminder.MaxSendCount = 1
|
||||
}
|
||||
} else {
|
||||
reminder.RepeatIntervalMinutes = 0
|
||||
reminder.MaxSendCount = 1
|
||||
}
|
||||
|
||||
_, err = models.Orm.Insert(&reminder)
|
||||
if err != nil {
|
||||
c.brJsonErr(500, 500, "创建提醒失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
c.brOk(map[string]interface{}{"schedule_id": schedID})
|
||||
}
|
||||
|
||||
// UpdateReminder PUT /backend/reminder/:id
|
||||
func (c *BackendReminderController) UpdateReminder() {
|
||||
claims, err := c.backendReminderClaims()
|
||||
if err != nil {
|
||||
c.brJsonErr(401, 401, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
idStr := c.Ctx.Input.Param(":id")
|
||||
id, _ := strconv.ParseUint(idStr, 10, 64)
|
||||
if id == 0 {
|
||||
c.brJsonErr(400, 400, "无效的ID")
|
||||
return
|
||||
}
|
||||
|
||||
raw, err := io.ReadAll(c.Ctx.Request.Body)
|
||||
if err != nil {
|
||||
c.brJsonErr(400, 400, "参数错误")
|
||||
return
|
||||
}
|
||||
var p backendReminderFormPayload
|
||||
if err := json.Unmarshal(raw, &p); err != nil {
|
||||
c.brJsonErr(400, 400, "参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
schedTime, err := time.ParseInLocation("2006-01-02 15:04:05", p.ScheduleTime, time.Local)
|
||||
if err != nil {
|
||||
c.brJsonErr(400, 400, "日程时间格式不合法")
|
||||
return
|
||||
}
|
||||
|
||||
var schedule models.BackendSchedule
|
||||
err = models.Orm.QueryTable(new(models.BackendSchedule)).
|
||||
Filter("id", id).
|
||||
Filter("tid", claims.TenantId).
|
||||
One(&schedule)
|
||||
if err != nil {
|
||||
c.brJsonErr(404, 404, "日程未找到")
|
||||
return
|
||||
}
|
||||
|
||||
var reminders []models.BackendScheduleReminder
|
||||
_, _ = models.Orm.QueryTable(new(models.BackendScheduleReminder)).
|
||||
Filter("schedule_id", id).
|
||||
Filter("is_deleted", 0).
|
||||
All(&reminders)
|
||||
isFinished := true
|
||||
if len(reminders) == 0 {
|
||||
isFinished = false
|
||||
} else {
|
||||
for _, r := range reminders {
|
||||
if r.RemindStatus != 2 {
|
||||
isFinished = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if isFinished {
|
||||
c.brJsonErr(400, 400, "该日程提醒已全部结束,无法编辑")
|
||||
return
|
||||
}
|
||||
|
||||
content := strings.TrimSpace(p.Content)
|
||||
if content == "" {
|
||||
content = schedule.Content
|
||||
}
|
||||
title := strings.SplitN(content, "\n", 2)[0]
|
||||
if len(title) > 60 {
|
||||
title = title[:60]
|
||||
}
|
||||
|
||||
schedule.Title = title
|
||||
schedule.Content = content
|
||||
schedule.ScheduleTime = schedTime
|
||||
_, err = models.Orm.Update(&schedule, "Title", "Content", "ScheduleTime")
|
||||
if err != nil {
|
||||
c.brJsonErr(500, 500, "更新失败")
|
||||
return
|
||||
}
|
||||
|
||||
_, _ = models.Orm.QueryTable(new(models.BackendScheduleReminder)).
|
||||
Filter("schedule_id", id).
|
||||
Update(map[string]interface{}{
|
||||
"IsDeleted": 1,
|
||||
"UpdateTime": time.Now(),
|
||||
})
|
||||
|
||||
for _, ch := range p.RemindChannels {
|
||||
ch = strings.ToUpper(strings.TrimSpace(ch))
|
||||
if ch != "SMS" && ch != "EMAIL" && ch != "BARK" && ch != "SITE_MSG" {
|
||||
continue
|
||||
}
|
||||
|
||||
targetVal := p.ReceiverTargets[ch]
|
||||
var target *string
|
||||
if targetVal != "" {
|
||||
target = &targetVal
|
||||
}
|
||||
|
||||
firstSendTime := schedTime.Add(-time.Duration(p.AdvanceMinutes) * time.Minute)
|
||||
|
||||
reminder := models.BackendScheduleReminder{
|
||||
Tid: claims.TenantId,
|
||||
ScheduleID: id,
|
||||
RemindChannel: ch,
|
||||
AdvanceMinutes: p.AdvanceMinutes,
|
||||
NextRemindTime: firstSendTime,
|
||||
ReceiverUserID: schedule.UserID,
|
||||
ReceiverTarget: target,
|
||||
RemindStatus: 0,
|
||||
CreateTime: time.Now(),
|
||||
UpdateTime: time.Now(),
|
||||
}
|
||||
|
||||
if ch == "EMAIL" || ch == "BARK" {
|
||||
token := backendGenerateToken()
|
||||
reminder.AckToken = &token
|
||||
reminder.RepeatIntervalMinutes = p.RepeatIntervalMinutes
|
||||
reminder.MaxSendCount = p.MaxSendCount
|
||||
if reminder.MaxSendCount <= 0 {
|
||||
reminder.MaxSendCount = 1
|
||||
}
|
||||
} else {
|
||||
reminder.RepeatIntervalMinutes = 0
|
||||
reminder.MaxSendCount = 1
|
||||
}
|
||||
|
||||
_, err = models.Orm.Insert(&reminder)
|
||||
if err != nil {
|
||||
c.brJsonErr(500, 500, "重新创建提醒失败")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
c.brOk(nil)
|
||||
}
|
||||
|
||||
// DeleteReminder DELETE /backend/reminder/:id
|
||||
func (c *BackendReminderController) DeleteReminder() {
|
||||
claims, err := c.backendReminderClaims()
|
||||
if err != nil {
|
||||
c.brJsonErr(401, 401, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
idStr := c.Ctx.Input.Param(":id")
|
||||
id, _ := strconv.ParseUint(idStr, 10, 64)
|
||||
if id == 0 {
|
||||
c.brJsonErr(400, 400, "无效的ID")
|
||||
return
|
||||
}
|
||||
|
||||
var schedule models.BackendSchedule
|
||||
err = models.Orm.QueryTable(new(models.BackendSchedule)).
|
||||
Filter("id", id).
|
||||
Filter("tid", claims.TenantId).
|
||||
One(&schedule)
|
||||
if err != nil {
|
||||
c.brJsonErr(404, 404, "日程未找到")
|
||||
return
|
||||
}
|
||||
|
||||
var reminders []models.BackendScheduleReminder
|
||||
_, _ = models.Orm.QueryTable(new(models.BackendScheduleReminder)).
|
||||
Filter("schedule_id", id).
|
||||
Filter("is_deleted", 0).
|
||||
All(&reminders)
|
||||
isFinished := true
|
||||
if len(reminders) == 0 {
|
||||
isFinished = false
|
||||
} else {
|
||||
for _, r := range reminders {
|
||||
if r.RemindStatus != 2 {
|
||||
isFinished = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if isFinished {
|
||||
c.brJsonErr(400, 400, "该日程提醒已全部结束,无法删除")
|
||||
return
|
||||
}
|
||||
|
||||
_, err = models.Orm.QueryTable(new(models.BackendSchedule)).Filter("id", id).Delete()
|
||||
if err == nil {
|
||||
_, _ = models.Orm.QueryTable(new(models.BackendScheduleReminder)).
|
||||
Filter("schedule_id", id).
|
||||
Update(map[string]interface{}{
|
||||
"IsDeleted": 1,
|
||||
"UpdateTime": time.Now(),
|
||||
})
|
||||
}
|
||||
|
||||
c.brOk(nil)
|
||||
}
|
||||
|
||||
type backendReminderBatchDeletePayload struct {
|
||||
Ids []uint64 `json:"ids"`
|
||||
}
|
||||
|
||||
// BatchDeleteReminder POST /backend/reminder/batchDelete
|
||||
func (c *BackendReminderController) BatchDeleteReminder() {
|
||||
claims, err := c.backendReminderClaims()
|
||||
if err != nil {
|
||||
c.brJsonErr(401, 401, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
raw, err := io.ReadAll(c.Ctx.Request.Body)
|
||||
if err != nil {
|
||||
c.brJsonErr(400, 400, "参数错误")
|
||||
return
|
||||
}
|
||||
var p backendReminderBatchDeletePayload
|
||||
if err := json.Unmarshal(raw, &p); err != nil {
|
||||
c.brJsonErr(400, 400, "参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
if len(p.Ids) == 0 {
|
||||
c.brOk(nil)
|
||||
return
|
||||
}
|
||||
|
||||
for _, scheduleID := range p.Ids {
|
||||
var reminders []models.BackendScheduleReminder
|
||||
_, _ = models.Orm.QueryTable(new(models.BackendScheduleReminder)).
|
||||
Filter("schedule_id", scheduleID).
|
||||
Filter("is_deleted", 0).
|
||||
All(&reminders)
|
||||
isFinished := true
|
||||
if len(reminders) == 0 {
|
||||
isFinished = false
|
||||
} else {
|
||||
for _, r := range reminders {
|
||||
if r.RemindStatus != 2 {
|
||||
isFinished = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if isFinished {
|
||||
c.brJsonErr(400, 400, fmt.Sprintf("日程ID %d 的提醒已全部结束,无法删除", scheduleID))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
_, _ = models.Orm.QueryTable(new(models.BackendSchedule)).
|
||||
Filter("id__in", p.Ids).
|
||||
Filter("tid", claims.TenantId).
|
||||
Delete()
|
||||
_, _ = models.Orm.QueryTable(new(models.BackendScheduleReminder)).
|
||||
Filter("schedule_id__in", p.Ids).
|
||||
Update(map[string]interface{}{
|
||||
"IsDeleted": 1,
|
||||
"UpdateTime": time.Now(),
|
||||
})
|
||||
|
||||
c.brOk(nil)
|
||||
}
|
||||
|
||||
// FinishReminder POST /backend/reminder/finish/:id
|
||||
func (c *BackendReminderController) FinishReminder() {
|
||||
claims, err := c.backendReminderClaims()
|
||||
if err != nil {
|
||||
c.brJsonErr(401, 401, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
idStr := c.Ctx.Input.Param(":id")
|
||||
id, _ := strconv.ParseUint(idStr, 10, 64)
|
||||
if id == 0 {
|
||||
c.brJsonErr(400, 400, "无效的ID")
|
||||
return
|
||||
}
|
||||
|
||||
var schedule models.BackendSchedule
|
||||
err = models.Orm.QueryTable(new(models.BackendSchedule)).
|
||||
Filter("id", id).
|
||||
Filter("tid", claims.TenantId).
|
||||
One(&schedule)
|
||||
if err != nil {
|
||||
c.brJsonErr(404, 404, "日程未找到")
|
||||
return
|
||||
}
|
||||
|
||||
var reminders []models.BackendScheduleReminder
|
||||
_, _ = models.Orm.QueryTable(new(models.BackendScheduleReminder)).
|
||||
Filter("schedule_id", id).
|
||||
Filter("is_deleted", 0).
|
||||
All(&reminders)
|
||||
|
||||
allFinished := len(reminders) > 0
|
||||
for _, r := range reminders {
|
||||
if r.RemindStatus != 2 {
|
||||
allFinished = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
var newStatus int8
|
||||
if allFinished {
|
||||
newStatus = 0
|
||||
} else {
|
||||
newStatus = 2
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
for _, r := range reminders {
|
||||
r.RemindStatus = newStatus
|
||||
r.UpdateTime = now
|
||||
models.Orm.Update(&r, "RemindStatus", "UpdateTime")
|
||||
}
|
||||
|
||||
c.brOk(map[string]interface{}{"is_finished": newStatus == 2})
|
||||
}
|
||||
|
||||
type backendReminderTestPayload struct {
|
||||
Title string `json:"title"`
|
||||
Content string `json:"content"`
|
||||
RemindChannels []string `json:"remind_channels"`
|
||||
}
|
||||
|
||||
// TestReminder POST /backend/reminder/test
|
||||
func (c *BackendReminderController) TestReminder() {
|
||||
claims, err := c.backendReminderClaims()
|
||||
if err != nil {
|
||||
c.brJsonErr(401, 401, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
raw, err := io.ReadAll(c.Ctx.Request.Body)
|
||||
if err != nil {
|
||||
c.brJsonErr(400, 400, "参数错误")
|
||||
return
|
||||
}
|
||||
var p backendReminderTestPayload
|
||||
if err := json.Unmarshal(raw, &p); err != nil {
|
||||
c.brJsonErr(400, 400, "参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
if strings.TrimSpace(p.Title) == "" {
|
||||
p.Title = "测试提醒"
|
||||
}
|
||||
if strings.TrimSpace(p.Content) == "" {
|
||||
p.Content = "这是一条验证日程提醒配置的测试通知。"
|
||||
}
|
||||
|
||||
senders := map[string]services.ReminderSender{
|
||||
"SMS": &services.SMSSender{},
|
||||
"EMAIL": &services.EmailSender{},
|
||||
"BARK": &services.BarkSender{},
|
||||
"SITE_MSG": &services.SiteMsgSender{},
|
||||
}
|
||||
|
||||
type TestResult struct {
|
||||
Channel string `json:"channel"`
|
||||
Success bool `json:"success"`
|
||||
Msg string `json:"msg"`
|
||||
}
|
||||
results := make([]TestResult, 0)
|
||||
|
||||
for _, ch := range p.RemindChannels {
|
||||
ch = strings.ToUpper(strings.TrimSpace(ch))
|
||||
sender, ok := senders[ch]
|
||||
if !ok {
|
||||
results = append(results, TestResult{Channel: ch, Success: false, Msg: "不支持的提醒渠道"})
|
||||
continue
|
||||
}
|
||||
|
||||
dummyToken := "test-token-for-verification"
|
||||
reminder := &models.PlatformScheduleReminder{
|
||||
RemindChannel: ch,
|
||||
ReceiverUserID: uint64(claims.UserID),
|
||||
AckToken: &dummyToken,
|
||||
}
|
||||
|
||||
success, sendErr := sender.Send(context.Background(), reminder, "[测试]"+p.Title, p.Content)
|
||||
msg := "发送成功"
|
||||
if !success {
|
||||
msg = "发送失败"
|
||||
if sendErr != nil {
|
||||
msg = sendErr.Error()
|
||||
}
|
||||
}
|
||||
results = append(results, TestResult{Channel: ch, Success: success, Msg: msg})
|
||||
}
|
||||
|
||||
c.brOk(results)
|
||||
}
|
||||
@@ -2,11 +2,13 @@ package middleware
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"server/models"
|
||||
"server/pkg/jwtutil"
|
||||
|
||||
"github.com/beego/beego/v2/server/web/context"
|
||||
)
|
||||
@@ -23,12 +25,96 @@ func BeginOperationLog(ctx *context.Context) {
|
||||
if shouldSkipLogging(method, url) {
|
||||
return
|
||||
}
|
||||
ctx.Input.SetData(oplogStartKey, time.Now())
|
||||
fmt.Printf("[oplog-begin] method=%s url=%s\n", method, url)
|
||||
|
||||
// 请求体由 main.go 的 CopyBody 保留在 Input.RequestBody
|
||||
// 解析用户信息(直接从 header 解析,不依赖 JWT 中间件设置的 context)
|
||||
var uid uint64
|
||||
var tid *uint64
|
||||
if auth := ctx.Request.Header.Get("Authorization"); auth != "" {
|
||||
parts := strings.SplitN(auth, " ", 2)
|
||||
if len(parts) == 2 && parts[0] == "Bearer" {
|
||||
if claims, err := jwtutil.ParseToken(parts[1]); err == nil {
|
||||
uid = uint64(claims.UserID)
|
||||
if claims.TenantId > 0 {
|
||||
tidVal := uint64(claims.TenantId)
|
||||
tid = &tidVal
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 采集请求信息
|
||||
reqBody := ""
|
||||
if rb := ctx.Input.RequestBody; len(rb) > 0 {
|
||||
s := string(rb)
|
||||
ctx.Input.SetData(oplogReqBodyKey, truncateString(maskSensitive(s), 5000))
|
||||
reqBody = truncateString(maskSensitive(string(rb)), 5000)
|
||||
} else if q := strings.TrimSpace(ctx.Request.URL.RawQuery); q != "" {
|
||||
reqBody = truncateString(maskSensitive(q), 5000)
|
||||
}
|
||||
|
||||
module := parseModule(url)
|
||||
action := parseAction(method, url)
|
||||
ip := truncateString(ctx.Input.IP(), 50)
|
||||
userAgent := truncateString(ctx.Request.Header.Get("User-Agent"), 500)
|
||||
|
||||
// 异步写入数据库
|
||||
go func() {
|
||||
time.Sleep(100 * time.Millisecond) // 等待响应完成
|
||||
logRow := &models.SystemOperationLog{
|
||||
Tid: tid,
|
||||
UserID: uid,
|
||||
Module: module,
|
||||
Action: action,
|
||||
Method: method,
|
||||
URL: truncateString(url, 255),
|
||||
IP: ip,
|
||||
UserAgent: userAgent,
|
||||
RequestData: strPtr(reqBody),
|
||||
Status: 1,
|
||||
ExecutionTime: 0,
|
||||
}
|
||||
_, err := models.Orm.Insert(logRow)
|
||||
if err != nil {
|
||||
fmt.Printf("[oplog] INSERT ERROR: %v\n", err)
|
||||
} else {
|
||||
fmt.Printf("[oplog] INSERTED uid=%d module=%s action=%s\n", uid, module, action)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func strPtr(s string) *string {
|
||||
if s == "" {
|
||||
return nil
|
||||
}
|
||||
return &s
|
||||
}
|
||||
|
||||
// WriteDeleteLog 控制器删除后直接写操作日志(带标题)
|
||||
func WriteDeleteLog(userID uint64, tid int, module, title string) {
|
||||
moduleCN := oplogModuleLabel(module)
|
||||
desc := fmt.Sprintf("您删除了%s:【%s】", moduleCN, truncateString(title, 20))
|
||||
tidVal := uint64(tid)
|
||||
logRow := &models.SystemOperationLog{
|
||||
UserID: userID,
|
||||
Tid: &tidVal,
|
||||
Module: module,
|
||||
Action: "删除",
|
||||
Method: "DELETE",
|
||||
URL: fmt.Sprintf("/app/%s", module),
|
||||
Status: 1,
|
||||
RequestData: &desc,
|
||||
ExecutionTime: 0,
|
||||
}
|
||||
models.Orm.Insert(logRow)
|
||||
}
|
||||
|
||||
func oplogModuleLabel(m string) string {
|
||||
switch m {
|
||||
case "notebook":
|
||||
return "记事本"
|
||||
case "schedule":
|
||||
return "日程提醒"
|
||||
default:
|
||||
return m
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +122,9 @@ func BeginOperationLog(ctx *context.Context) {
|
||||
func FinishOperationLog(ctx *context.Context) {
|
||||
url := ctx.Input.URL()
|
||||
method := ctx.Input.Method()
|
||||
fmt.Printf("[oplog-finish] method=%s url=%s\n", method, url)
|
||||
if shouldSkipLogging(method, url) {
|
||||
fmt.Printf("[oplog-finish] SKIPPED\n")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -48,6 +136,22 @@ func FinishOperationLog(ctx *context.Context) {
|
||||
|
||||
uid := parseUint64FromCtx(ctx.Input.GetData("userId"))
|
||||
tidVal := parseUint64FromCtx(ctx.Input.GetData("tenantId"))
|
||||
|
||||
// 如果 context 中没有用户信息,尝试从 Authorization header 解析
|
||||
if uid == 0 {
|
||||
if auth := ctx.Request.Header.Get("Authorization"); auth != "" {
|
||||
parts := strings.SplitN(auth, " ", 2)
|
||||
if len(parts) == 2 && parts[0] == "Bearer" {
|
||||
if claims, err := jwtutil.ParseToken(parts[1]); err == nil {
|
||||
uid = uint64(claims.UserID)
|
||||
if claims.TenantId > 0 {
|
||||
tidVal = uint64(claims.TenantId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var tid *uint64
|
||||
if tidVal > 0 {
|
||||
tid = &tidVal
|
||||
@@ -100,7 +204,11 @@ func FinishOperationLog(ctx *context.Context) {
|
||||
ErrorMessage: errMsg,
|
||||
ExecutionTime: execSec,
|
||||
}
|
||||
_, _ = models.Orm.Insert(logRow)
|
||||
fmt.Printf("[oplog] method=%s url=%s uid=%d tid=%v module=%s action=%s\n", method, url, uid, tid, module, action)
|
||||
_, err := models.Orm.Insert(logRow)
|
||||
if err != nil {
|
||||
fmt.Printf("[oplog] INSERT ERROR: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func parseAction(method, url string) string {
|
||||
@@ -183,6 +291,14 @@ func shouldSkipLogging(method, url string) bool {
|
||||
if strings.HasPrefix(url, "/api/softwareupgrade/check") {
|
||||
return true
|
||||
}
|
||||
// App 端读接口跳过(只记录写操作:POST/PUT/DELETE)
|
||||
if strings.HasPrefix(url, "/app/") && method == "GET" {
|
||||
return true
|
||||
}
|
||||
// App 端 DELETE 由控制器自行记录(需要标题信息)
|
||||
if strings.HasPrefix(url, "/app/") && method == "DELETE" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package models
|
||||
|
||||
import "time"
|
||||
|
||||
// BackendNotebook 租户记事本表: yz_backend_notebook
|
||||
type BackendNotebook struct {
|
||||
ID uint64 `orm:"column(id);pk;auto" json:"id"`
|
||||
Tid int `orm:"column(tid)" json:"tid"`
|
||||
Title string `orm:"column(title);size(255)" json:"title"`
|
||||
Content string `orm:"column(content);type(longtext);null" json:"content"`
|
||||
Pinned int8 `orm:"column(pinned);default(0)" json:"pinned"`
|
||||
UserID *uint64 `orm:"column(user_id);null" json:"user_id"`
|
||||
UserName *string `orm:"column(user_name);size(100);null" json:"user_name"`
|
||||
IsDeleted int8 `orm:"column(is_deleted);default(0)" json:"is_deleted"`
|
||||
CreateTime time.Time `orm:"column(create_time);auto_now_add;type(datetime)" json:"create_time"`
|
||||
UpdateTime *time.Time `orm:"column(update_time);type(datetime);null" json:"update_time"`
|
||||
DeleteTime *time.Time `orm:"column(delete_time);type(datetime);null" json:"delete_time"`
|
||||
}
|
||||
|
||||
func (m *BackendNotebook) TableName() string {
|
||||
return "yz_backend_notebook"
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package models
|
||||
|
||||
import "time"
|
||||
|
||||
// BackendSchedule 租户日程主表: yz_backend_schedule
|
||||
type BackendSchedule struct {
|
||||
ID uint64 `orm:"column(id);pk;auto" json:"id"`
|
||||
Tid int `orm:"column(tid)" json:"tid"`
|
||||
Title string `orm:"column(title);size(255)" json:"title"`
|
||||
Content string `orm:"column(content);type(text)" json:"content"`
|
||||
ScheduleTime time.Time `orm:"column(schedule_time);type(datetime)" json:"schedule_time"`
|
||||
UserID uint64 `orm:"column(user_id)" json:"user_id"`
|
||||
}
|
||||
|
||||
func (m *BackendSchedule) TableName() string {
|
||||
return "yz_backend_schedule"
|
||||
}
|
||||
|
||||
// BackendScheduleReminder 租户日程提醒: yz_backend_schedule_reminder
|
||||
type BackendScheduleReminder struct {
|
||||
ID uint64 `orm:"column(id);pk;auto" json:"id"`
|
||||
Tid int `orm:"column(tid)" json:"tid"`
|
||||
ScheduleID uint64 `orm:"column(schedule_id)" json:"schedule_id"`
|
||||
RemindChannel string `orm:"column(remind_channel);size(20)" json:"remind_channel"`
|
||||
AdvanceMinutes int `orm:"column(advance_minutes);default(0)" json:"advance_minutes"`
|
||||
RepeatIntervalMinutes int `orm:"column(repeat_interval_minutes);default(0)" json:"repeat_interval_minutes"`
|
||||
NextRemindTime time.Time `orm:"column(next_remind_time);type(datetime)" json:"next_remind_time"`
|
||||
SendCount int `orm:"column(send_count);default(0)" json:"send_count"`
|
||||
MaxSendCount int `orm:"column(max_send_count);default(1)" json:"max_send_count"`
|
||||
AckToken *string `orm:"column(ack_token);size(64);null" json:"ack_token"`
|
||||
AckStatus int8 `orm:"column(ack_status);default(0)" json:"ack_status"`
|
||||
AckTime *time.Time `orm:"column(ack_time);type(datetime);null" json:"ack_time"`
|
||||
ReceiverUserID uint64 `orm:"column(receiver_user_id)" json:"receiver_user_id"`
|
||||
ReceiverTarget *string `orm:"column(receiver_target);size(255);null" json:"receiver_target"`
|
||||
RemindStatus int8 `orm:"column(remind_status);default(0)" json:"remind_status"`
|
||||
ScanLock string `orm:"column(scan_lock);size(64);default('')" json:"scan_lock"`
|
||||
IsDeleted int8 `orm:"column(is_deleted);default(0)" json:"is_deleted"`
|
||||
CreateTime time.Time `orm:"column(create_time);auto_now_add;type(datetime)" json:"create_time"`
|
||||
UpdateTime time.Time `orm:"column(update_time);auto_now;type(datetime)" json:"update_time"`
|
||||
}
|
||||
|
||||
func (m *BackendScheduleReminder) TableName() string {
|
||||
return "yz_backend_schedule_reminder"
|
||||
}
|
||||
|
||||
// BackendScheduleReminderSendLog 租户提醒发送流水: yz_backend_schedule_reminder_send_log
|
||||
type BackendScheduleReminderSendLog struct {
|
||||
ID uint64 `orm:"column(id);pk;auto" json:"id"`
|
||||
Tid int `orm:"column(tid)" json:"tid"`
|
||||
ReminderID uint64 `orm:"column(reminder_id)" json:"reminder_id"`
|
||||
SendTime time.Time `orm:"column(send_time);type(datetime)" json:"send_time"`
|
||||
SendResult int8 `orm:"column(send_result)" json:"send_result"`
|
||||
FailReason *string `orm:"column(fail_reason);size(255);null" json:"fail_reason"`
|
||||
}
|
||||
|
||||
func (m *BackendScheduleReminderSendLog) TableName() string {
|
||||
return "yz_backend_schedule_reminder_send_log"
|
||||
}
|
||||
@@ -74,6 +74,11 @@ func Init(_ string) {
|
||||
new(PlatformSchedule),
|
||||
new(PlatformScheduleReminder),
|
||||
new(PlatformScheduleReminderSendLog),
|
||||
|
||||
new(BackendNotebook),
|
||||
new(BackendSchedule),
|
||||
new(BackendScheduleReminder),
|
||||
new(BackendScheduleReminderSendLog),
|
||||
)
|
||||
|
||||
// 创建全局 Ormer
|
||||
|
||||
@@ -30,4 +30,19 @@ func Register() {
|
||||
// 注册
|
||||
beego.Router("/app/register", &controllers.AppAuthController{}, "post:Register")
|
||||
beego.Router("/app/sendRegisterCode", &controllers.AppAuthController{}, "post:SendRegisterCode")
|
||||
|
||||
// 日程提醒
|
||||
beego.Router("/app/schedule/list", &controllers.AppReminderController{}, "get:GetList")
|
||||
beego.Router("/app/schedule", &controllers.AppReminderController{}, "post:Create")
|
||||
beego.Router("/app/schedule/:id", &controllers.AppReminderController{}, "get:GetDetail;put:Update;delete:Delete")
|
||||
beego.Router("/app/schedule/:id/toggle", &controllers.AppReminderController{}, "post:ToggleComplete")
|
||||
|
||||
// 记事本
|
||||
beego.Router("/app/notebook/list", &controllers.AppNotebookController{}, "get:GetList")
|
||||
beego.Router("/app/notebook", &controllers.AppNotebookController{}, "post:Create")
|
||||
beego.Router("/app/notebook/:id", &controllers.AppNotebookController{}, "get:GetDetail;put:Update;delete:Delete")
|
||||
beego.Router("/app/notebook/:id/togglePin", &controllers.AppNotebookController{}, "post:TogglePin")
|
||||
|
||||
// 活动日志
|
||||
beego.Router("/app/activity/list", &controllers.AppActivityController{}, "get:GetList")
|
||||
}
|
||||
|
||||
@@ -144,4 +144,18 @@ func RegisterAuthRoutes() {
|
||||
beego.Router("/backend/domain/tenant/toggleStatus", &controllers.BackendTenantDomainController{}, "post:ToggleStatus")
|
||||
beego.Router("/backend/domain/tenant/delete/:id", &controllers.BackendTenantDomainController{}, "delete:Delete")
|
||||
|
||||
// 记事本管理
|
||||
beego.Router("/backend/notebook/list", &controllers.BackendNotebookController{}, "get:List")
|
||||
beego.Router("/backend/notebook/detail/:id", &controllers.BackendNotebookController{}, "get:Detail")
|
||||
beego.Router("/backend/notebook/create", &controllers.BackendNotebookController{}, "post:Create")
|
||||
beego.Router("/backend/notebook/update/:id", &controllers.BackendNotebookController{}, "post:Update")
|
||||
beego.Router("/backend/notebook/delete/:id", &controllers.BackendNotebookController{}, "delete:Delete")
|
||||
|
||||
// 日程提醒管理
|
||||
beego.Router("/backend/reminder/list", &controllers.BackendReminderController{}, "get:GetReminderList")
|
||||
beego.Router("/backend/reminder/test", &controllers.BackendReminderController{}, "post:TestReminder")
|
||||
beego.Router("/backend/reminder/:id", &controllers.BackendReminderController{}, "get:GetReminderDetail;put:UpdateReminder;delete:DeleteReminder")
|
||||
beego.Router("/backend/reminder", &controllers.BackendReminderController{}, "post:CreateReminder")
|
||||
beego.Router("/backend/reminder/batchDelete", &controllers.BackendReminderController{}, "post:BatchDeleteReminder")
|
||||
beego.Router("/backend/reminder/finish/:id", &controllers.BackendReminderController{}, "post:FinishReminder")
|
||||
}
|
||||
|
||||
@@ -32,10 +32,8 @@ func init() {
|
||||
}
|
||||
})
|
||||
|
||||
// 全局操作日志:请求开始采集
|
||||
// 全局操作日志:请求开始采集并异步写入
|
||||
beego.InsertFilter("*", beego.BeforeRouter, middleware.BeginOperationLog)
|
||||
// 全局操作日志:请求结束统一落库
|
||||
beego.InsertFilter("*", beego.FinishRouter, middleware.FinishOperationLog)
|
||||
|
||||
// 根据运行模式选择要注册的路由组
|
||||
// 优先读取环境变量 APP_MODE,其次读取配置 app_mode,默认 all
|
||||
|
||||
Reference in New Issue
Block a user