更新
This commit is contained in:
1 parent
012f8edf8b
commit
36ff960fbd
33 files changed
+1644
-344
No files matched your search
@@ -125,6 +125,117 @@ func (c *AppAggregateController) Home() {
|
||||
})
|
||||
}
|
||||
|
||||
// publicModuleItem 移动端免登录可见的模块卡片:只暴露展示与安全开关,不含菜单/KPI。
|
||||
// 供未登录用户渲染 nav 宫格,require_login 决定点击是「进登录引导」还是「直接进入」。
|
||||
type publicModuleItem struct {
|
||||
ID uint64 `json:"id"`
|
||||
Code string `json:"code"`
|
||||
Name string `json:"name"`
|
||||
Icon string `json:"icon"`
|
||||
Path string `json:"path"`
|
||||
Description string `json:"description"`
|
||||
RequireLogin int8 `json:"require_login"`
|
||||
}
|
||||
|
||||
// PublicModules GET /api/modules/public
|
||||
//
|
||||
// 公开接口(无需登录,路径不在 jwt 中间件 protectedPrefixes 内):
|
||||
// 返回移动端可见的模块宫格 + 每个模块的 require_login 标记,
|
||||
// 让未登录用户也能进入 nav 默认页并分辨「哪些能免登录用、哪些必须先登录」。
|
||||
func (c *AppAggregateController) PublicModules() {
|
||||
var rows []models.SystemModules
|
||||
if _, err := models.Orm.QueryTable(new(models.SystemModules)).
|
||||
Filter("delete_time__isnull", true).
|
||||
Filter("status", 1).
|
||||
Filter("is_show", 1).
|
||||
OrderBy("sort", "id").
|
||||
All(&rows); err != nil {
|
||||
c.serveJSON(map[string]interface{}{"code": 200, "msg": "success", "data": []publicModuleItem{}})
|
||||
return
|
||||
}
|
||||
// 移动端只展示 app 可见模块(未配置 platforms 视为全端)
|
||||
rows = services.FilterModulesByPlatform(rows, models.PlatformApp)
|
||||
|
||||
out := make([]publicModuleItem, 0, len(rows))
|
||||
for _, m := range rows {
|
||||
out = append(out, publicModuleItem{
|
||||
ID: m.ID,
|
||||
Code: m.Code,
|
||||
Name: m.Name,
|
||||
Icon: m.Icon,
|
||||
Path: m.Path,
|
||||
Description: m.Description,
|
||||
RequireLogin: m.RequireLogin,
|
||||
})
|
||||
}
|
||||
c.serveJSON(map[string]interface{}{"code": 200, "msg": "success", "data": out})
|
||||
}
|
||||
|
||||
// PublicMenus GET /api/menus/public
|
||||
//
|
||||
// 公开接口(无需登录):返回移动端免登录菜单(require_login=0)。
|
||||
// 免登录控制按菜单粒度配置(Platform 菜单管理),游客持游客令牌直接使用;
|
||||
// nav 宫格据此判定模块卡片是否可免登录进入(模块级 require_login 不再作为判定依据)。
|
||||
// 不做角色/套餐过滤:免登录功能对所有人开放,未配 platforms 的菜单继承所属模块。
|
||||
func (c *AppAggregateController) PublicMenus() {
|
||||
var rows []models.SystemMenu
|
||||
if _, err := models.Orm.QueryTable(new(models.SystemMenu)).
|
||||
Filter("status", 1).
|
||||
Filter("require_login", 0).
|
||||
All(&rows); err != nil {
|
||||
c.serveJSON(map[string]interface{}{"code": 200, "msg": "success", "data": []appMenuItem{}})
|
||||
return
|
||||
}
|
||||
|
||||
allModules := c.allModules()
|
||||
sortedModules := services.SortedModulesForScope(allModules)
|
||||
|
||||
visible := make([]models.SystemMenu, 0, len(rows))
|
||||
for _, m := range rows {
|
||||
if m.Type == 3 { // 接口类菜单是权限标识载体,不下发
|
||||
continue
|
||||
}
|
||||
if m.IsVisible != nil && *m.IsVisible == 0 {
|
||||
continue
|
||||
}
|
||||
if !services.MenuVisibleOn(m, sortedModules, allModules, models.PlatformApp) {
|
||||
continue
|
||||
}
|
||||
visible = append(visible, m)
|
||||
}
|
||||
|
||||
// 公开集合内统计子节点数:目录若只包含未放开(或不存在)的子菜单,leaf 为 true 但不跳任何页,
|
||||
// 前端按 NATIVE_RULES 解析不到路由时会落「开发中」占位,不会误入受保护页。
|
||||
childCount := map[int64]int{}
|
||||
for _, m := range visible {
|
||||
if m.Pid != 0 {
|
||||
childCount[m.Pid]++
|
||||
}
|
||||
}
|
||||
|
||||
out := make([]appMenuItem, 0, len(visible))
|
||||
for _, m := range visible {
|
||||
item := appMenuItem{
|
||||
ID: m.ID,
|
||||
Pid: m.Pid,
|
||||
Type: m.Type,
|
||||
Sort: m.Sort,
|
||||
Title: strings.TrimSpace(m.Title),
|
||||
}
|
||||
if m.Path != nil {
|
||||
item.Path = strings.TrimSpace(*m.Path)
|
||||
}
|
||||
if m.Icon != nil {
|
||||
item.Icon = strings.TrimSpace(*m.Icon)
|
||||
}
|
||||
item.Module = appModuleCodeOfPath(item.Path, allModules)
|
||||
item.Leaf = childCount[int64(m.ID)] == 0
|
||||
out = append(out, item)
|
||||
}
|
||||
sort.SliceStable(out, func(i, j int) bool { return out[i].Sort < out[j].Sort })
|
||||
c.serveJSON(map[string]interface{}{"code": 200, "msg": "success", "data": out})
|
||||
}
|
||||
|
||||
// appQuickMenuIds 读取当前用户自定义的常用功能菜单 ID 列表(有序)。
|
||||
// 未设置或解析失败一律返回空数组,前端据此回落到默认取前 N 个叶子菜单。
|
||||
func (c *AppAggregateController) appQuickMenuIds(tid, uid uint64) []uint64 {
|
||||
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
"server/models"
|
||||
"server/pkg/identity"
|
||||
"server/pkg/jwtutil"
|
||||
"server/services/gueststore"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -19,9 +21,9 @@ type AppNotebookController struct {
|
||||
beego.Controller
|
||||
}
|
||||
|
||||
// 只放行移动端与租户端令牌:/api/** 仅被 uniapp 调用,平台令牌不该能打移动端接口。
|
||||
// 放行移动端、租户端与游客令牌:游客调用时不进业务表,分流到 gueststore。
|
||||
func (c *AppNotebookController) appNotebookClaims() (*jwtutil.Claims, error) {
|
||||
return identity.Claims(c.Ctx.Request.Header.Get("Authorization"), identity.EndApp, identity.EndBackend)
|
||||
return identity.Claims(c.Ctx.Request.Header.Get("Authorization"), identity.EndApp, identity.EndBackend, identity.EndGuest)
|
||||
}
|
||||
|
||||
func (c *AppNotebookController) nbJsonErr(httpStatus, bizCode int, msg string) {
|
||||
@@ -45,6 +47,11 @@ func (c *AppNotebookController) GetList() {
|
||||
|
||||
keyword := strings.TrimSpace(c.GetString("keyword"))
|
||||
|
||||
if identity.IsGuest(claims) {
|
||||
c.guestList(claims, keyword)
|
||||
return
|
||||
}
|
||||
|
||||
qs := models.Orm.QueryTable(new(models.BackendNotebook)).
|
||||
Filter("is_deleted", 0).
|
||||
Filter("user_id", claims.UserID)
|
||||
@@ -109,6 +116,11 @@ func (c *AppNotebookController) GetDetail() {
|
||||
return
|
||||
}
|
||||
|
||||
if identity.IsGuest(claims) {
|
||||
c.guestDetail(claims, id)
|
||||
return
|
||||
}
|
||||
|
||||
var note models.BackendNotebook
|
||||
err = models.Orm.QueryTable(new(models.BackendNotebook)).
|
||||
Filter("id", id).
|
||||
@@ -164,6 +176,11 @@ func (c *AppNotebookController) Create() {
|
||||
title = "无标题"
|
||||
}
|
||||
|
||||
if identity.IsGuest(claims) {
|
||||
c.guestCreateItem(claims, title, payload.Content, payload.Pinned)
|
||||
return
|
||||
}
|
||||
|
||||
userID := uint64(claims.UserID)
|
||||
pinned := int8(0)
|
||||
if payload.Pinned {
|
||||
@@ -224,6 +241,11 @@ func (c *AppNotebookController) Update() {
|
||||
return
|
||||
}
|
||||
|
||||
if identity.IsGuest(claims) {
|
||||
c.guestUpdateItem(claims, id, payload.Title, payload.Content, payload.Pinned)
|
||||
return
|
||||
}
|
||||
|
||||
var note models.BackendNotebook
|
||||
err = models.Orm.QueryTable(new(models.BackendNotebook)).
|
||||
Filter("id", id).
|
||||
@@ -286,6 +308,19 @@ func (c *AppNotebookController) Delete() {
|
||||
return
|
||||
}
|
||||
|
||||
if identity.IsGuest(claims) {
|
||||
if err := gueststore.Delete(uint64(claims.UserID), guestModuleNotebook, id); err != nil {
|
||||
if err == gueststore.ErrNotFound {
|
||||
c.nbJsonErr(404, 404, "笔记不存在")
|
||||
return
|
||||
}
|
||||
c.nbJsonErr(500, 500, "删除失败")
|
||||
return
|
||||
}
|
||||
c.nbOk(nil)
|
||||
return
|
||||
}
|
||||
|
||||
var note models.BackendNotebook
|
||||
err = models.Orm.QueryTable(new(models.BackendNotebook)).
|
||||
Filter("id", id).
|
||||
@@ -327,6 +362,11 @@ func (c *AppNotebookController) TogglePin() {
|
||||
return
|
||||
}
|
||||
|
||||
if identity.IsGuest(claims) {
|
||||
c.guestTogglePin(claims, id)
|
||||
return
|
||||
}
|
||||
|
||||
var note models.BackendNotebook
|
||||
err = models.Orm.QueryTable(new(models.BackendNotebook)).
|
||||
Filter("id", id).
|
||||
@@ -361,3 +401,107 @@ func (c *AppNotebookController) TogglePin() {
|
||||
"pinned": newPinned == 1,
|
||||
})
|
||||
}
|
||||
|
||||
// ---------- 游客分支(免登录态):数据存 yz_guest_data,响应结构与登录态一致 ----------
|
||||
|
||||
// guestNoteFull 读取并补全一条游客笔记(ID/时间以存储行为准)。
|
||||
func guestNoteFull(r gueststore.Row) guestNoteItem {
|
||||
var it guestNoteItem
|
||||
if json.Unmarshal(r.Payload, &it) != nil {
|
||||
it = guestNoteItem{Title: "无标题"}
|
||||
}
|
||||
it.ID = r.ID
|
||||
it.CreatedAt = r.CreateTime.Format("2006-01-02 15:04:05")
|
||||
it.UpdatedAt = it.CreatedAt
|
||||
if r.UpdateTime != nil {
|
||||
it.UpdatedAt = r.UpdateTime.Format("2006-01-02 15:04:05")
|
||||
}
|
||||
return it
|
||||
}
|
||||
|
||||
func (c *AppNotebookController) guestList(claims *jwtutil.Claims, keyword string) {
|
||||
rows, err := gueststore.List(uint64(claims.UserID), guestModuleNotebook)
|
||||
if err != nil {
|
||||
c.nbJsonErr(500, 500, "查询失败")
|
||||
return
|
||||
}
|
||||
items := make([]guestNoteItem, 0, len(rows))
|
||||
for _, r := range rows {
|
||||
it := guestNoteFull(r)
|
||||
if keyword != "" && !strings.Contains(it.Title, keyword) {
|
||||
continue
|
||||
}
|
||||
items = append(items, it)
|
||||
}
|
||||
// 与登录态一致:置顶优先,再按更新时间倒序
|
||||
sort.SliceStable(items, func(i, j int) bool {
|
||||
if items[i].Pinned != items[j].Pinned {
|
||||
return items[i].Pinned
|
||||
}
|
||||
return items[i].UpdatedAt > items[j].UpdatedAt
|
||||
})
|
||||
c.nbOk(map[string]interface{}{"list": items, "total": len(items)})
|
||||
}
|
||||
|
||||
func (c *AppNotebookController) guestDetail(claims *jwtutil.Claims, id uint64) {
|
||||
r, err := gueststore.Get(uint64(claims.UserID), guestModuleNotebook, id)
|
||||
if err != nil {
|
||||
c.nbJsonErr(404, 404, "笔记不存在")
|
||||
return
|
||||
}
|
||||
c.nbOk(guestNoteFull(r))
|
||||
}
|
||||
|
||||
func (c *AppNotebookController) guestCreateItem(claims *jwtutil.Claims, title, content string, pinned bool) {
|
||||
now := time.Now().Format("2006-01-02 15:04:05")
|
||||
it := guestNoteItem{Title: title, Content: content, Pinned: pinned, CreatedAt: now, UpdatedAt: now}
|
||||
newID, err := gueststore.Insert(uint64(claims.UserID), guestModuleNotebook, it)
|
||||
if err != nil {
|
||||
c.nbJsonErr(500, 500, "创建失败")
|
||||
return
|
||||
}
|
||||
c.nbOk(map[string]interface{}{
|
||||
"id": newID, "title": title, "content": content, "pinned": pinned,
|
||||
})
|
||||
}
|
||||
|
||||
func (c *AppNotebookController) guestUpdateItem(claims *jwtutil.Claims, id uint64, title, content string, pinned *bool) {
|
||||
r, err := gueststore.Get(uint64(claims.UserID), guestModuleNotebook, id)
|
||||
if err != nil {
|
||||
c.nbJsonErr(404, 404, "笔记不存在")
|
||||
return
|
||||
}
|
||||
it := guestNoteFull(r)
|
||||
if t := strings.TrimSpace(title); t != "" {
|
||||
it.Title = t
|
||||
}
|
||||
it.Content = content
|
||||
if pinned != nil {
|
||||
it.Pinned = *pinned
|
||||
}
|
||||
it.UpdatedAt = time.Now().Format("2006-01-02 15:04:05")
|
||||
if err = gueststore.Update(uint64(claims.UserID), guestModuleNotebook, id, it); err != nil {
|
||||
c.nbJsonErr(500, 500, "更新失败")
|
||||
return
|
||||
}
|
||||
c.nbOk(map[string]interface{}{
|
||||
"id": it.ID, "title": it.Title, "content": it.Content, "pinned": it.Pinned,
|
||||
"created_at": it.CreatedAt, "updated_at": it.UpdatedAt,
|
||||
})
|
||||
}
|
||||
|
||||
func (c *AppNotebookController) guestTogglePin(claims *jwtutil.Claims, id uint64) {
|
||||
r, err := gueststore.Get(uint64(claims.UserID), guestModuleNotebook, id)
|
||||
if err != nil {
|
||||
c.nbJsonErr(404, 404, "笔记不存在")
|
||||
return
|
||||
}
|
||||
it := guestNoteFull(r)
|
||||
it.Pinned = !it.Pinned
|
||||
it.UpdatedAt = time.Now().Format("2006-01-02 15:04:05")
|
||||
if err = gueststore.Update(uint64(claims.UserID), guestModuleNotebook, id, it); err != nil {
|
||||
c.nbJsonErr(500, 500, "操作失败")
|
||||
return
|
||||
}
|
||||
c.nbOk(map[string]interface{}{"pinned": it.Pinned})
|
||||
}
|
||||
@@ -1,13 +1,16 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"server/middleware"
|
||||
"server/models"
|
||||
"server/pkg/identity"
|
||||
"server/pkg/jwtutil"
|
||||
"server/services/gueststore"
|
||||
|
||||
"github.com/beego/beego/v2/client/orm"
|
||||
"github.com/beego/beego/v2/core/logs"
|
||||
@@ -24,12 +27,18 @@ type AppPasswordStoreController struct {
|
||||
beego.Controller
|
||||
}
|
||||
|
||||
// 只放行移动端与租户端令牌:/api/** 仅被 uniapp 调用,平台令牌不该能打移动端接口。
|
||||
// 放行移动端、租户端与游客令牌:游客 tid=0 合法,数据进 gueststore 而非业务表。
|
||||
func (c *AppPasswordStoreController) appPwdClaims() (*jwtutil.Claims, error) {
|
||||
claims, err := identity.Claims(c.Ctx.Request.Header.Get("Authorization"), identity.EndApp, identity.EndBackend)
|
||||
claims, err := identity.Claims(c.Ctx.Request.Header.Get("Authorization"), identity.EndApp, identity.EndBackend, identity.EndGuest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if identity.IsGuest(claims) {
|
||||
if claims.UserID <= 0 {
|
||||
return nil, orm.ErrNoRows
|
||||
}
|
||||
return claims, nil
|
||||
}
|
||||
if claims.UserID <= 0 || claims.TenantId <= 0 {
|
||||
return nil, orm.ErrNoRows
|
||||
}
|
||||
@@ -43,6 +52,10 @@ func (c *AppPasswordStoreController) GetList() {
|
||||
passwordReply(&c.Controller, 401, "未登录或无权限", nil)
|
||||
return
|
||||
}
|
||||
if identity.IsGuest(cl) {
|
||||
c.guestList(cl, strings.TrimSpace(c.GetString("keyword")))
|
||||
return
|
||||
}
|
||||
qs := models.Orm.QueryTable(new(models.BackendPasswordStore)).
|
||||
Filter("is_deleted", 0).Filter("tid", cl.TenantId).Filter("user_id", cl.UserID)
|
||||
if k := strings.TrimSpace(c.GetString("keyword")); k != "" {
|
||||
@@ -82,6 +95,10 @@ func (c *AppPasswordStoreController) GetDetail() {
|
||||
passwordReply(&c.Controller, 400, "参数错误", nil)
|
||||
return
|
||||
}
|
||||
if identity.IsGuest(cl) {
|
||||
c.guestDetail(cl, uint64(id))
|
||||
return
|
||||
}
|
||||
var m models.BackendPasswordStore
|
||||
e = models.Orm.QueryTable(new(models.BackendPasswordStore)).
|
||||
Filter("id", id).Filter("is_deleted", 0).Filter("tid", cl.TenantId).Filter("user_id", cl.UserID).One(&m)
|
||||
@@ -121,6 +138,10 @@ func (c *AppPasswordStoreController) Create() {
|
||||
passwordReply(&c.Controller, 400, e.Error(), nil)
|
||||
return
|
||||
}
|
||||
if identity.IsGuest(cl) {
|
||||
c.guestCreate(cl, p)
|
||||
return
|
||||
}
|
||||
qs := models.Orm.QueryTable(new(models.BackendPasswordStore)).
|
||||
Filter("is_deleted", 0).Filter("tid", cl.TenantId).Filter("user_id", cl.UserID)
|
||||
if existing, e := findSamePlatformRecord(qs, 0, p.Platform, p.URL); e != nil {
|
||||
@@ -172,6 +193,10 @@ func (c *AppPasswordStoreController) Update() {
|
||||
passwordReply(&c.Controller, 400, e.Error(), nil)
|
||||
return
|
||||
}
|
||||
if identity.IsGuest(cl) {
|
||||
c.guestUpdate(cl, uint64(id), p)
|
||||
return
|
||||
}
|
||||
var m models.BackendPasswordStore
|
||||
e = models.Orm.QueryTable(new(models.BackendPasswordStore)).
|
||||
Filter("id", id).Filter("is_deleted", 0).Filter("tid", cl.TenantId).Filter("user_id", cl.UserID).One(&m)
|
||||
@@ -221,6 +246,18 @@ func (c *AppPasswordStoreController) Delete() {
|
||||
passwordReply(&c.Controller, 400, "参数错误", nil)
|
||||
return
|
||||
}
|
||||
if identity.IsGuest(cl) {
|
||||
if err := gueststore.Delete(uint64(cl.UserID), guestModulePasswordStore, uint64(id)); err != nil {
|
||||
if err == gueststore.ErrNotFound {
|
||||
passwordReply(&c.Controller, 404, "记录不存在", nil)
|
||||
return
|
||||
}
|
||||
passwordReply(&c.Controller, 500, "删除失败", nil)
|
||||
return
|
||||
}
|
||||
passwordReply(&c.Controller, 200, "success", nil)
|
||||
return
|
||||
}
|
||||
var m models.BackendPasswordStore
|
||||
e = models.Orm.QueryTable(new(models.BackendPasswordStore)).
|
||||
Filter("id", id).Filter("is_deleted", 0).Filter("tid", cl.TenantId).Filter("user_id", cl.UserID).One(&m)
|
||||
@@ -241,3 +278,125 @@ func (c *AppPasswordStoreController) Delete() {
|
||||
middleware.WriteDeleteLog(uint64(cl.UserID), cl.TenantId, "passwordStore", m.Platform)
|
||||
passwordReply(&c.Controller, 200, "success", nil)
|
||||
}
|
||||
|
||||
// ---------- 游客分支(免登录态):数据存 yz_guest_data,响应结构与登录态一致 ----------
|
||||
|
||||
func guestPwdFull(r gueststore.Row) PasswordStoreItemDTO {
|
||||
var it PasswordStoreItemDTO
|
||||
if json.Unmarshal(r.Payload, &it) != nil {
|
||||
it = PasswordStoreItemDTO{}
|
||||
}
|
||||
it.ID = r.ID
|
||||
it.Tid = 0
|
||||
it.UserID = 0
|
||||
it.CreateTime = r.CreateTime
|
||||
it.UpdateTime = r.UpdateTime
|
||||
if it.Accounts == nil {
|
||||
it.Accounts = []models.PasswordAccountItem{}
|
||||
}
|
||||
return it
|
||||
}
|
||||
|
||||
// guestPwdSamePlatform 与登录态 findSamePlatformRecord 同语义:平台+网址重复。
|
||||
func guestPwdSamePlatform(it PasswordStoreItemDTO, platform, url string, excludeID uint64) bool {
|
||||
if it.ID == excludeID {
|
||||
return false
|
||||
}
|
||||
return strings.EqualFold(strings.TrimSpace(it.Platform), strings.TrimSpace(platform)) &&
|
||||
strings.TrimSpace(it.URL) == strings.TrimSpace(url)
|
||||
}
|
||||
|
||||
func guestPwdKeywordHit(it PasswordStoreItemDTO, k string) bool {
|
||||
k = strings.ToLower(k)
|
||||
if strings.Contains(strings.ToLower(it.Platform), k) ||
|
||||
strings.Contains(strings.ToLower(it.URL), k) ||
|
||||
strings.Contains(strings.ToLower(it.Remark), k) {
|
||||
return true
|
||||
}
|
||||
for _, a := range it.Accounts {
|
||||
if strings.Contains(strings.ToLower(a.Username), k) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *AppPasswordStoreController) guestList(cl *jwtutil.Claims, keyword string) {
|
||||
rows, err := gueststore.List(uint64(cl.UserID), guestModulePasswordStore)
|
||||
if err != nil {
|
||||
passwordReply(&c.Controller, 500, "查询失败", nil)
|
||||
return
|
||||
}
|
||||
res := make([]PasswordStoreItemDTO, 0, len(rows))
|
||||
for _, r := range rows {
|
||||
it := guestPwdFull(r)
|
||||
if keyword != "" && !guestPwdKeywordHit(it, keyword) {
|
||||
continue
|
||||
}
|
||||
res = append(res, it)
|
||||
}
|
||||
passwordReply(&c.Controller, 200, "success", map[string]interface{}{"list": res, "total": len(res)})
|
||||
}
|
||||
|
||||
func (c *AppPasswordStoreController) guestDetail(cl *jwtutil.Claims, id uint64) {
|
||||
r, err := gueststore.Get(uint64(cl.UserID), guestModulePasswordStore, id)
|
||||
if err != nil {
|
||||
passwordReply(&c.Controller, 404, "记录不存在", nil)
|
||||
return
|
||||
}
|
||||
passwordReply(&c.Controller, 200, "success", guestPwdFull(r))
|
||||
}
|
||||
|
||||
func (c *AppPasswordStoreController) guestCreate(cl *jwtutil.Claims, p passwordPayload) {
|
||||
rows, err := gueststore.List(uint64(cl.UserID), guestModulePasswordStore)
|
||||
if err != nil {
|
||||
passwordReply(&c.Controller, 500, "查询失败", nil)
|
||||
return
|
||||
}
|
||||
for _, r := range rows {
|
||||
if guestPwdSamePlatform(guestPwdFull(r), p.Platform, p.URL, 0) {
|
||||
passwordReply(&c.Controller, 400, "同平台同网址记录已存在,请勿重复添加", nil)
|
||||
return
|
||||
}
|
||||
}
|
||||
it := PasswordStoreItemDTO{
|
||||
Platform: p.Platform, URL: p.URL, Accounts: p.Accounts, Remark: p.Remark,
|
||||
CreateTime: time.Now(),
|
||||
}
|
||||
newID, err := gueststore.Insert(uint64(cl.UserID), guestModulePasswordStore, it)
|
||||
if err != nil {
|
||||
passwordReply(&c.Controller, 500, "创建失败", nil)
|
||||
return
|
||||
}
|
||||
it.ID = newID
|
||||
passwordReply(&c.Controller, 200, "success", it)
|
||||
}
|
||||
|
||||
func (c *AppPasswordStoreController) guestUpdate(cl *jwtutil.Claims, id uint64, p passwordPayload) {
|
||||
r, err := gueststore.Get(uint64(cl.UserID), guestModulePasswordStore, id)
|
||||
if err != nil {
|
||||
passwordReply(&c.Controller, 404, "记录不存在", nil)
|
||||
return
|
||||
}
|
||||
rows, err := gueststore.List(uint64(cl.UserID), guestModulePasswordStore)
|
||||
if err != nil {
|
||||
passwordReply(&c.Controller, 500, "查询失败", nil)
|
||||
return
|
||||
}
|
||||
for _, rr := range rows {
|
||||
if guestPwdSamePlatform(guestPwdFull(rr), p.Platform, p.URL, id) {
|
||||
passwordReply(&c.Controller, 400, "同平台同网址记录已存在,请勿重复添加", nil)
|
||||
return
|
||||
}
|
||||
}
|
||||
it := guestPwdFull(r)
|
||||
it.Platform = p.Platform
|
||||
it.URL = p.URL
|
||||
it.Accounts = p.Accounts
|
||||
it.Remark = p.Remark
|
||||
if err = gueststore.Update(uint64(cl.UserID), guestModulePasswordStore, id, it); err != nil {
|
||||
passwordReply(&c.Controller, 500, "更新失败", nil)
|
||||
return
|
||||
}
|
||||
passwordReply(&c.Controller, 200, "success", it)
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"server/models"
|
||||
"server/pkg/identity"
|
||||
"server/pkg/jwtutil"
|
||||
"server/services/gueststore"
|
||||
|
||||
"github.com/beego/beego/v2/client/orm"
|
||||
beego "github.com/beego/beego/v2/server/web"
|
||||
@@ -22,9 +23,9 @@ type AppReminderController struct {
|
||||
beego.Controller
|
||||
}
|
||||
|
||||
// 只放行移动端与租户端令牌:/api/** 仅被 uniapp 调用,平台令牌不该能打移动端接口。
|
||||
// 放行移动端、租户端与游客令牌:游客调用时不进业务表,分流到 gueststore。
|
||||
func (c *AppReminderController) appClaims() (*jwtutil.Claims, error) {
|
||||
return identity.Claims(c.Ctx.Request.Header.Get("Authorization"), identity.EndApp, identity.EndBackend)
|
||||
return identity.Claims(c.Ctx.Request.Header.Get("Authorization"), identity.EndApp, identity.EndBackend, identity.EndGuest)
|
||||
}
|
||||
|
||||
func (c *AppReminderController) jsonErr(httpStatus, bizCode int, msg string) {
|
||||
@@ -75,6 +76,11 @@ func (c *AppReminderController) GetList() {
|
||||
pageSize = 20
|
||||
}
|
||||
|
||||
if identity.IsGuest(claims) {
|
||||
c.guestList(claims, page, pageSize, keyword, status)
|
||||
return
|
||||
}
|
||||
|
||||
qs := models.Orm.QueryTable(new(models.BackendSchedule)).
|
||||
Filter("user_id", claims.UserID).
|
||||
Filter("tid", claims.TenantId)
|
||||
@@ -171,6 +177,11 @@ func (c *AppReminderController) GetDetail() {
|
||||
return
|
||||
}
|
||||
|
||||
if identity.IsGuest(claims) {
|
||||
c.guestDetail(claims, id)
|
||||
return
|
||||
}
|
||||
|
||||
var schedule models.BackendSchedule
|
||||
err = models.Orm.QueryTable(new(models.BackendSchedule)).
|
||||
Filter("id", id).
|
||||
@@ -272,6 +283,11 @@ func (c *AppReminderController) Create() {
|
||||
title = "日程提醒"
|
||||
}
|
||||
|
||||
if identity.IsGuest(claims) {
|
||||
c.guestCreate(claims, p, content, schedTime)
|
||||
return
|
||||
}
|
||||
|
||||
schedule := models.BackendSchedule{
|
||||
Tid: claims.TenantId,
|
||||
Title: title,
|
||||
@@ -363,6 +379,11 @@ func (c *AppReminderController) Update() {
|
||||
return
|
||||
}
|
||||
|
||||
if identity.IsGuest(claims) {
|
||||
c.guestUpdate(claims, id, p)
|
||||
return
|
||||
}
|
||||
|
||||
var schedule models.BackendSchedule
|
||||
err = models.Orm.QueryTable(new(models.BackendSchedule)).
|
||||
Filter("id", id).
|
||||
@@ -482,6 +503,11 @@ func (c *AppReminderController) Delete() {
|
||||
return
|
||||
}
|
||||
|
||||
if identity.IsGuest(claims) {
|
||||
c.guestDelete(claims, id)
|
||||
return
|
||||
}
|
||||
|
||||
var schedule models.BackendSchedule
|
||||
err = models.Orm.QueryTable(new(models.BackendSchedule)).
|
||||
Filter("id", id).
|
||||
@@ -543,6 +569,11 @@ func (c *AppReminderController) ToggleComplete() {
|
||||
return
|
||||
}
|
||||
|
||||
if identity.IsGuest(claims) {
|
||||
c.guestToggle(claims, id)
|
||||
return
|
||||
}
|
||||
|
||||
var schedule models.BackendSchedule
|
||||
err = models.Orm.QueryTable(new(models.BackendSchedule)).
|
||||
Filter("id", id).
|
||||
@@ -587,3 +618,175 @@ func (c *AppReminderController) ToggleComplete() {
|
||||
"is_finished": newStatus == 2,
|
||||
})
|
||||
}
|
||||
|
||||
// ---------- 游客分支(免登录态):数据存 yz_guest_data,响应结构与登录态一致。
|
||||
// 游客日程不接提醒发送链路(无短信/邮件推送),仅作待办记录;转正合并时重建提醒。 ----------
|
||||
|
||||
func guestScheduleItemMap(it guestScheduleItem) map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"id": it.ID,
|
||||
"content": it.Content,
|
||||
"schedule_time": it.ScheduleTime,
|
||||
"remind_channels": it.RemindChannels,
|
||||
"is_finished": it.IsFinished,
|
||||
"advance_minutes": it.AdvanceMinutes,
|
||||
"repeat_interval_minutes": it.RepeatIntervalMinutes,
|
||||
"max_send_count": it.MaxSendCount,
|
||||
"created_at": it.CreatedAt,
|
||||
"updated_at": it.UpdatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
func guestScheduleFull(r gueststore.Row) guestScheduleItem {
|
||||
var it guestScheduleItem
|
||||
if json.Unmarshal(r.Payload, &it) != nil {
|
||||
it = guestScheduleItem{Content: "(数据异常)"}
|
||||
}
|
||||
it.ID = r.ID
|
||||
it.CreatedAt = r.CreateTime.Format("2006-01-02 15:04:05")
|
||||
it.UpdatedAt = it.CreatedAt
|
||||
if r.UpdateTime != nil {
|
||||
it.UpdatedAt = r.UpdateTime.Format("2006-01-02 15:04:05")
|
||||
}
|
||||
if it.RemindChannels == nil {
|
||||
it.RemindChannels = []string{}
|
||||
}
|
||||
return it
|
||||
}
|
||||
|
||||
func (c *AppReminderController) guestList(claims *jwtutil.Claims, page, pageSize int, keyword, status string) {
|
||||
rows, err := gueststore.List(uint64(claims.UserID), guestModuleSchedule)
|
||||
if err != nil {
|
||||
c.jsonErr(500, 500, "查询失败")
|
||||
return
|
||||
}
|
||||
items := make([]map[string]interface{}, 0, len(rows))
|
||||
for _, r := range rows {
|
||||
it := guestScheduleFull(r)
|
||||
if keyword != "" && !strings.Contains(it.Content, keyword) {
|
||||
continue
|
||||
}
|
||||
if status == "pending" && it.IsFinished {
|
||||
continue
|
||||
}
|
||||
if status == "done" && !it.IsFinished {
|
||||
continue
|
||||
}
|
||||
items = append(items, guestScheduleItemMap(it))
|
||||
}
|
||||
total := len(items)
|
||||
start := (page - 1) * pageSize
|
||||
if start > total {
|
||||
start = total
|
||||
}
|
||||
end := start + pageSize
|
||||
if end > total {
|
||||
end = total
|
||||
}
|
||||
c.ok(map[string]interface{}{
|
||||
"list": items[start:end], "total": total, "page": page, "pageSize": pageSize,
|
||||
})
|
||||
}
|
||||
|
||||
func (c *AppReminderController) guestDetail(claims *jwtutil.Claims, id uint64) {
|
||||
r, err := gueststore.Get(uint64(claims.UserID), guestModuleSchedule, id)
|
||||
if err != nil {
|
||||
c.jsonErr(404, 404, "日程未找到")
|
||||
return
|
||||
}
|
||||
c.ok(guestScheduleItemMap(guestScheduleFull(r)))
|
||||
}
|
||||
|
||||
func (c *AppReminderController) guestCreate(claims *jwtutil.Claims, p appSchedulePayload, content string, schedTime time.Time) {
|
||||
_ = schedTime
|
||||
now := time.Now().Format("2006-01-02 15:04:05")
|
||||
channels := p.RemindChannels
|
||||
if channels == nil {
|
||||
channels = []string{}
|
||||
}
|
||||
it := guestScheduleItem{
|
||||
Content: content,
|
||||
ScheduleTime: p.ScheduleTime,
|
||||
RemindChannels: channels,
|
||||
AdvanceMinutes: p.AdvanceMinutes,
|
||||
RepeatIntervalMinutes: p.RepeatIntervalMinutes,
|
||||
MaxSendCount: p.MaxSendCount,
|
||||
IsFinished: false,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
newID, err := gueststore.Insert(uint64(claims.UserID), guestModuleSchedule, it)
|
||||
if err != nil {
|
||||
c.jsonErr(500, 500, "保存日程失败")
|
||||
return
|
||||
}
|
||||
c.ok(map[string]interface{}{"id": newID})
|
||||
}
|
||||
|
||||
func (c *AppReminderController) guestUpdate(claims *jwtutil.Claims, id uint64, p appSchedulePayload) {
|
||||
r, err := gueststore.Get(uint64(claims.UserID), guestModuleSchedule, id)
|
||||
if err != nil {
|
||||
c.jsonErr(404, 404, "日程未找到")
|
||||
return
|
||||
}
|
||||
it := guestScheduleFull(r)
|
||||
if it.IsFinished {
|
||||
c.jsonErr(400, 400, "该日程提醒已全部结束,无法编辑")
|
||||
return
|
||||
}
|
||||
if content := strings.TrimSpace(p.Content); content != "" {
|
||||
it.Content = content
|
||||
}
|
||||
if strings.TrimSpace(p.ScheduleTime) != "" {
|
||||
if _, perr := time.ParseInLocation("2006-01-02 15:04:05", p.ScheduleTime, time.Local); perr != nil {
|
||||
c.jsonErr(400, 400, "日程时间格式不合法")
|
||||
return
|
||||
}
|
||||
it.ScheduleTime = p.ScheduleTime
|
||||
}
|
||||
if p.RemindChannels != nil {
|
||||
it.RemindChannels = p.RemindChannels
|
||||
}
|
||||
it.AdvanceMinutes = p.AdvanceMinutes
|
||||
it.RepeatIntervalMinutes = p.RepeatIntervalMinutes
|
||||
it.MaxSendCount = p.MaxSendCount
|
||||
it.UpdatedAt = time.Now().Format("2006-01-02 15:04:05")
|
||||
if err = gueststore.Update(uint64(claims.UserID), guestModuleSchedule, id, it); err != nil {
|
||||
c.jsonErr(500, 500, "更新失败")
|
||||
return
|
||||
}
|
||||
c.ok(nil)
|
||||
}
|
||||
|
||||
func (c *AppReminderController) guestDelete(claims *jwtutil.Claims, id uint64) {
|
||||
r, err := gueststore.Get(uint64(claims.UserID), guestModuleSchedule, id)
|
||||
if err != nil {
|
||||
c.jsonErr(404, 404, "日程未找到")
|
||||
return
|
||||
}
|
||||
if guestScheduleFull(r).IsFinished {
|
||||
c.jsonErr(400, 400, "该日程提醒已全部结束,无法删除")
|
||||
return
|
||||
}
|
||||
if err = gueststore.Delete(uint64(claims.UserID), guestModuleSchedule, id); err != nil {
|
||||
c.jsonErr(500, 500, "删除失败")
|
||||
return
|
||||
}
|
||||
c.ok(nil)
|
||||
}
|
||||
|
||||
func (c *AppReminderController) guestToggle(claims *jwtutil.Claims, id uint64) {
|
||||
r, err := gueststore.Get(uint64(claims.UserID), guestModuleSchedule, id)
|
||||
if err != nil {
|
||||
c.jsonErr(404, 404, "日程未找到")
|
||||
return
|
||||
}
|
||||
it := guestScheduleFull(r)
|
||||
it.IsFinished = !it.IsFinished
|
||||
it.UpdatedAt = time.Now().Format("2006-01-02 15:04:05")
|
||||
if err = gueststore.Update(uint64(claims.UserID), guestModuleSchedule, id, it); err != nil {
|
||||
c.jsonErr(500, 500, "操作失败")
|
||||
return
|
||||
}
|
||||
c.ok(map[string]interface{}{"is_finished": it.IsFinished})
|
||||
}
|
||||
@@ -76,6 +76,7 @@ type menuPayload struct {
|
||||
Sort *int64 `json:"sort"`
|
||||
Status *int8 `json:"status"`
|
||||
IsVisible *int8 `json:"is_visible"`
|
||||
RequireLogin *int8 `json:"require_login"` // 1=必须登录(默认) 0=可免登录(游客可用)
|
||||
Views []int `json:"views"`
|
||||
Platforms []string `json:"platforms"` // 展示端:["web"] ["app"] ["web","app"];空=继承所属模块
|
||||
Type *int8 `json:"type"`
|
||||
@@ -417,6 +418,7 @@ type menuNode struct {
|
||||
Sort int64 `json:"sort"`
|
||||
Status int8 `json:"status"`
|
||||
IsVisible *int8 `json:"is_visible,omitempty"`
|
||||
RequireLogin int8 `json:"require_login"` // 1=必须登录 0=可免登录(游客)
|
||||
Views []int `json:"views,omitempty"`
|
||||
Platforms []string `json:"platforms,omitempty"` // 展示端,空=继承所属模块
|
||||
Type int8 `json:"type"`
|
||||
@@ -430,15 +432,16 @@ func buildMenuTree(menus []models.SystemMenu, pid int64) []*menuNode {
|
||||
for _, m := range menus {
|
||||
if m.Pid == pid {
|
||||
node := &menuNode{
|
||||
ID: m.ID,
|
||||
Pid: m.Pid,
|
||||
Title: m.Title,
|
||||
Sort: m.Sort,
|
||||
Status: m.Status,
|
||||
IsVisible: m.IsVisible,
|
||||
Views: parseViews(m.Views),
|
||||
Platforms: services.ParsePlatforms(m.Platforms),
|
||||
Type: m.Type,
|
||||
ID: m.ID,
|
||||
Pid: m.Pid,
|
||||
Title: m.Title,
|
||||
Sort: m.Sort,
|
||||
Status: m.Status,
|
||||
IsVisible: m.IsVisible,
|
||||
RequireLogin: m.RequireLogin,
|
||||
Views: parseViews(m.Views),
|
||||
Platforms: services.ParsePlatforms(m.Platforms),
|
||||
Type: m.Type,
|
||||
}
|
||||
if m.Path != nil {
|
||||
node.Path = *m.Path
|
||||
@@ -514,6 +517,7 @@ func (c *BackendMenuController) CreateMenu() {
|
||||
Sort: valueInt64(payload.Sort, 0),
|
||||
Status: valueInt8(payload.Status, 1),
|
||||
IsVisible: ptrInt8(valueInt8(payload.IsVisible, 1)),
|
||||
RequireLogin: valueInt8(payload.RequireLogin, 1),
|
||||
Views: &viewsStr,
|
||||
Type: valueInt8(payload.Type, 1),
|
||||
Path: ptrString(valueString(payload.Path, "")),
|
||||
@@ -565,6 +569,7 @@ func (c *BackendMenuController) UpdateMenu() {
|
||||
"sort": valueInt64(payload.Sort, 0),
|
||||
"status": valueInt8(payload.Status, 1),
|
||||
"is_visible": valueInt8(payload.IsVisible, 1),
|
||||
"require_login": valueInt8(payload.RequireLogin, 1),
|
||||
"views": string(viewsBytes),
|
||||
"platforms": services.NormalizePlatforms(payload.Platforms),
|
||||
"type": valueInt8(payload.Type, 1),
|
||||
|
||||
@@ -52,7 +52,6 @@ func (c *PlatformModulesController) modulesClaims() (*jwtutil.Claims, error) {
|
||||
return identity.ClaimsByPath(c.Ctx.Request.Header.Get("Authorization"), c.Ctx.Request.URL.Path)
|
||||
}
|
||||
|
||||
|
||||
func (c *PlatformModulesController) jsonErr(httpStatus, bizCode int, msg string) {
|
||||
c.Ctx.Output.SetStatus(httpStatus)
|
||||
c.Data["json"] = map[string]interface{}{"code": bizCode, "msg": msg}
|
||||
@@ -79,18 +78,19 @@ func (c *PlatformModulesController) GetList() {
|
||||
list := make([]map[string]interface{}, 0, len(rows))
|
||||
for i := range rows {
|
||||
list = append(list, map[string]interface{}{
|
||||
"id": rows[i].ID,
|
||||
"mid": rows[i].Mid,
|
||||
"name": rows[i].Name,
|
||||
"code": rows[i].Code,
|
||||
"path": rows[i].Path,
|
||||
"icon": rows[i].Icon,
|
||||
"description": rows[i].Description,
|
||||
"type": rows[i].Type,
|
||||
"sort": rows[i].Sort,
|
||||
"status": rows[i].Status,
|
||||
"is_show": rows[i].IsShow,
|
||||
"platforms": platformList(&rows[i]),
|
||||
"id": rows[i].ID,
|
||||
"mid": rows[i].Mid,
|
||||
"name": rows[i].Name,
|
||||
"code": rows[i].Code,
|
||||
"path": rows[i].Path,
|
||||
"icon": rows[i].Icon,
|
||||
"description": rows[i].Description,
|
||||
"type": rows[i].Type,
|
||||
"sort": rows[i].Sort,
|
||||
"status": rows[i].Status,
|
||||
"is_show": rows[i].IsShow,
|
||||
"require_login": rows[i].RequireLogin,
|
||||
"platforms": platformList(&rows[i]),
|
||||
})
|
||||
}
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
@@ -156,22 +156,54 @@ func (c *PlatformModulesController) GetDetail() {
|
||||
c.jsonErr(404, 404, "模块不存在")
|
||||
return
|
||||
}
|
||||
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "获取成功", "data": row}
|
||||
// 与 GetList 保持一致的规范化输出:platforms 统一为数组、带上 require_login。
|
||||
// 否则详情回填时 platforms 是原始 JSON 字符串,编辑提交会因 []string 反序列化失败报 400。
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"code": 200,
|
||||
"msg": "获取成功",
|
||||
"data": map[string]interface{}{
|
||||
"id": row.ID,
|
||||
"mid": row.Mid,
|
||||
"name": row.Name,
|
||||
"code": row.Code,
|
||||
"path": row.Path,
|
||||
"icon": row.Icon,
|
||||
"description": row.Description,
|
||||
"type": row.Type,
|
||||
"sort": row.Sort,
|
||||
"status": row.Status,
|
||||
"is_show": row.IsShow,
|
||||
"require_login": row.RequireLogin,
|
||||
"platforms": platformList(&row),
|
||||
},
|
||||
}
|
||||
_ = c.ServeJSON()
|
||||
}
|
||||
|
||||
type modulePayload struct {
|
||||
Mid *uint64 `json:"mid"`
|
||||
Name string `json:"name"`
|
||||
Code string `json:"code"`
|
||||
Path string `json:"path"`
|
||||
Icon string `json:"icon"`
|
||||
Description string `json:"description"`
|
||||
Type int `json:"type"`
|
||||
Sort int `json:"sort"`
|
||||
Status int8 `json:"status"`
|
||||
IsShow int8 `json:"is_show"`
|
||||
Platforms []string `json:"platforms"` // 展示端:["web"] ["app"] ["web","app"];空=全端
|
||||
Mid *uint64 `json:"mid"`
|
||||
Name string `json:"name"`
|
||||
Code string `json:"code"`
|
||||
Path string `json:"path"`
|
||||
Icon string `json:"icon"`
|
||||
Description string `json:"description"`
|
||||
Type int `json:"type"`
|
||||
Sort int `json:"sort"`
|
||||
Status int8 `json:"status"`
|
||||
IsShow int8 `json:"is_show"`
|
||||
Platforms []string `json:"platforms"` // 展示端:["web"] ["app"] ["web","app"];空=全端
|
||||
RequireLogin *int8 `json:"require_login"` // 是否必须登录:1=必须 0=可免登录;指针以区分「未传」(默认 1)
|
||||
}
|
||||
|
||||
// resolveRequireLogin 未传该字段时回落为 1(必须登录),保持既有行为不变。
|
||||
func resolveRequireLogin(v *int8) int8 {
|
||||
if v == nil {
|
||||
return 1
|
||||
}
|
||||
if *v != 0 && *v != 1 {
|
||||
return 1
|
||||
}
|
||||
return *v
|
||||
}
|
||||
|
||||
// Add POST /platform/modules
|
||||
@@ -213,18 +245,19 @@ func (c *PlatformModulesController) Add() {
|
||||
now := time.Now()
|
||||
platforms := services.NormalizePlatforms(p.Platforms)
|
||||
row := &models.SystemModules{
|
||||
Mid: p.Mid,
|
||||
Name: p.Name,
|
||||
Code: p.Code,
|
||||
Path: strings.TrimSpace(p.Path),
|
||||
Icon: strings.TrimSpace(p.Icon),
|
||||
Description: strings.TrimSpace(p.Description),
|
||||
Type: p.Type,
|
||||
Sort: p.Sort,
|
||||
Status: p.Status,
|
||||
IsShow: p.IsShow,
|
||||
CreateTime: &now,
|
||||
UpdateTime: &now,
|
||||
Mid: p.Mid,
|
||||
Name: p.Name,
|
||||
Code: p.Code,
|
||||
Path: strings.TrimSpace(p.Path),
|
||||
Icon: strings.TrimSpace(p.Icon),
|
||||
Description: strings.TrimSpace(p.Description),
|
||||
Type: p.Type,
|
||||
Sort: p.Sort,
|
||||
Status: p.Status,
|
||||
IsShow: p.IsShow,
|
||||
RequireLogin: resolveRequireLogin(p.RequireLogin),
|
||||
CreateTime: &now,
|
||||
UpdateTime: &now,
|
||||
}
|
||||
if platforms != "" {
|
||||
row.Platforms = &platforms
|
||||
@@ -284,18 +317,19 @@ func (c *PlatformModulesController) Edit() {
|
||||
now := time.Now()
|
||||
// 展示端为空串表示「全端」,需显式落库以覆盖历史值
|
||||
up := map[string]interface{}{
|
||||
"mid": p.Mid,
|
||||
"name": p.Name,
|
||||
"code": p.Code,
|
||||
"path": strings.TrimSpace(p.Path),
|
||||
"icon": strings.TrimSpace(p.Icon),
|
||||
"description": strings.TrimSpace(p.Description),
|
||||
"type": p.Type,
|
||||
"sort": p.Sort,
|
||||
"status": p.Status,
|
||||
"is_show": p.IsShow,
|
||||
"platforms": services.NormalizePlatforms(p.Platforms),
|
||||
"update_time": now,
|
||||
"mid": p.Mid,
|
||||
"name": p.Name,
|
||||
"code": p.Code,
|
||||
"path": strings.TrimSpace(p.Path),
|
||||
"icon": strings.TrimSpace(p.Icon),
|
||||
"description": strings.TrimSpace(p.Description),
|
||||
"type": p.Type,
|
||||
"sort": p.Sort,
|
||||
"status": p.Status,
|
||||
"is_show": p.IsShow,
|
||||
"require_login": resolveRequireLogin(p.RequireLogin),
|
||||
"platforms": services.NormalizePlatforms(p.Platforms),
|
||||
"update_time": now,
|
||||
}
|
||||
n, err := models.Orm.QueryTable(new(models.SystemModules)).
|
||||
Filter("id", id).
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
-- =============================================================
|
||||
-- yz_system_menu 增加「是否必须登录」字段 require_login
|
||||
--
|
||||
-- 用途:免登录控制从模块级下沉到菜单级——Platform 菜单管理逐条配置
|
||||
-- 某菜单(功能入口)是否允许移动端游客(持游客令牌)免登录使用。
|
||||
-- 1 = 必须登录(默认,保持既有行为)
|
||||
-- 0 = 可免登录使用(进入 nav 公开菜单 /api/menus/public)
|
||||
--
|
||||
-- 关联:yz_system_modules.require_login 保留但移动端不再作为判定依据,
|
||||
-- 以菜单级开关为准(模块宫格是否可点 = 该模块下存在免登录菜单)。
|
||||
--
|
||||
-- 幂等:MySQL 的 ALTER TABLE ADD COLUMN 不支持 IF NOT EXISTS,
|
||||
-- 重复执行会报 1060 Duplicate column name。这里用 information_schema
|
||||
-- 判断列是否存在,缺失才拼 ADD COLUMN,再通过 PREPARE 执行。
|
||||
-- 约定:本脚本在 mysql 客户端手动执行(同一会话保持 PREPARE 生效)。
|
||||
-- =============================================================
|
||||
|
||||
SET @sql := CONCAT(
|
||||
'ALTER TABLE `yz_system_menu` ',
|
||||
IF(
|
||||
(SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 'yz_system_menu'
|
||||
AND COLUMN_NAME = 'require_login') = 0,
|
||||
'ADD COLUMN `require_login` TINYINT NOT NULL DEFAULT 1 COMMENT ''1=必须登录 0=可免登录使用'' AFTER `is_visible`',
|
||||
''
|
||||
)
|
||||
);
|
||||
|
||||
-- 列已存在时 @sql 仅剩 'ALTER TABLE `yz_system_menu` ',替换为无害语句
|
||||
SET @sql := IF(RIGHT(TRIM(@sql), 1) = '`', 'SELECT 1 AS column_already_exists', @sql);
|
||||
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
-- =============================================================
|
||||
-- 初始放开「便捷工具」的三个免登录功能(确认菜单标题后再执行):
|
||||
--
|
||||
-- SELECT id, title, path FROM `yz_system_menu`
|
||||
-- WHERE title IN ('记事本', '日程提醒', '账号管理') AND delete_time IS NULL;
|
||||
--
|
||||
-- UPDATE `yz_system_menu`
|
||||
-- SET `require_login` = 0
|
||||
-- WHERE title IN ('记事本', '日程提醒', '账号管理');
|
||||
--
|
||||
-- 说明:父级目录菜单如需游客可见,也要一并置 0;
|
||||
-- 后续新模块按需放开,全部走 Platform 菜单管理界面配置。
|
||||
-- =============================================================
|
||||
@@ -0,0 +1,46 @@
|
||||
-- =============================================================
|
||||
-- yz_system_modules 增加「是否必须登录」字段 require_login
|
||||
--
|
||||
-- 用途:Platform 模块管理可配置某业务模块是否允许移动端免登录访问。
|
||||
-- 1 = 必须登录(默认,保持既有行为)
|
||||
-- 0 = 可免登录使用
|
||||
--
|
||||
-- 幂等:MySQL 的 ALTER TABLE ADD COLUMN 不支持 IF NOT EXISTS,
|
||||
-- 重复执行会报 1060 Duplicate column name。这里用 information_schema
|
||||
-- 判断列是否存在,缺失才拼 ADD COLUMN,再通过 PREPARE 执行。
|
||||
-- 约定:本脚本在 mysql 客户端手动执行(同一会话保持 PREPARE 生效)。
|
||||
-- =============================================================
|
||||
|
||||
SET @sql := CONCAT(
|
||||
'ALTER TABLE `yz_system_modules` ',
|
||||
IF(
|
||||
(SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 'yz_system_modules'
|
||||
AND COLUMN_NAME = 'require_login') = 0,
|
||||
'ADD COLUMN `require_login` TINYINT NOT NULL DEFAULT 1 COMMENT ''1=必须登录 0=可免登录使用'' AFTER `is_show`',
|
||||
''
|
||||
)
|
||||
);
|
||||
|
||||
-- 列已存在时 @sql 仅剩 'ALTER TABLE `yz_system_modules` ',替换为无害语句
|
||||
SET @sql := IF(RIGHT(TRIM(@sql), 1) = '`', 'SELECT 1 AS column_already_exists', @sql);
|
||||
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
-- =============================================================
|
||||
-- 方案 B(兜底):不支持会话变量 / PREPARE 的执行器(工单系统等),
|
||||
-- 改用下面的纯静态语句。注意:本条不幂等,列已存在会报
|
||||
-- 1060 Duplicate column,执行前先用 information_schema 自查:
|
||||
--
|
||||
-- SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
-- WHERE TABLE_SCHEMA = DATABASE()
|
||||
-- AND TABLE_NAME = 'yz_system_modules'
|
||||
-- AND COLUMN_NAME = 'require_login'; -- 返回 0 才执行 ALTER
|
||||
--
|
||||
-- ALTER TABLE `yz_system_modules`
|
||||
-- ADD COLUMN `require_login` TINYINT NOT NULL DEFAULT 1
|
||||
-- COMMENT '1=必须登录 0=可免登录使用' AFTER `is_show`;
|
||||
-- =============================================================
|
||||
@@ -0,0 +1,33 @@
|
||||
-- =============================================================
|
||||
-- 游客(免登录模块)身份与通用数据表
|
||||
-- 对应代码:models.GuestUser / models.GuestData、services/gueststore
|
||||
-- 幂等:CREATE TABLE IF NOT EXISTS,可重复执行
|
||||
-- 用途:小程序用手机号授权换取“游客轻身份”,免登录模块产生的数据
|
||||
-- 按 guest_id + module 存入 yz_guest_data(文档式,永久保留不清理);
|
||||
-- 正式登录后按手机号把数据合并进正式账号(merged_uid 标记,行置 is_deleted=2)。
|
||||
-- =============================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `yz_guest_user` (
|
||||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`phone` VARCHAR(20) NOT NULL DEFAULT '' COMMENT '微信验证手机号(游客唯一标识)',
|
||||
`openid` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '小程序 openid(可选溯源)',
|
||||
`unionid` VARCHAR(64) NOT NULL DEFAULT '' COMMENT 'unionid(可为空)',
|
||||
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '首次授权时间',
|
||||
`last_active_time` DATETIME NULL COMMENT '最近一次签发/活动时间',
|
||||
`merged_uid` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '转正并入的 AuthIdentity.ID,0=未合并',
|
||||
`merged_time` DATETIME NULL COMMENT '合并时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_phone` (`phone`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='游客轻身份(微信手机号授权签发)';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `yz_guest_data` (
|
||||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`guest_id` BIGINT UNSIGNED NOT NULL COMMENT 'yz_guest_user.id',
|
||||
`module` VARCHAR(50) NOT NULL DEFAULT '' COMMENT '所属业务模块:notebook / schedule / passwordStore / …',
|
||||
`payload` TEXT NOT NULL COMMENT '业务条目 JSON 序列化(结构由各模块自定义)',
|
||||
`is_deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '0正常 1已删除(保留不清理) 2已合并进正式账号',
|
||||
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`update_time` DATETIME NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_guest_module` (`guest_id`, `module`, `is_deleted`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='游客通用数据(按模块文档式存储,永久保留)';
|
||||
@@ -77,6 +77,9 @@ func Init(_ string) {
|
||||
new(SystemDomainPool),
|
||||
new(SystemTenantDomain),
|
||||
new(SystemModules),
|
||||
// 游客轻身份与通用数据(docs/sql/create_guest_tables.sql):免登录模块的临时存储
|
||||
new(GuestUser),
|
||||
new(GuestData),
|
||||
new(StorageConfig),
|
||||
new(TenantSiteSetting),
|
||||
new(PlatformSiteSetting),
|
||||
|
||||
+21
-19
@@ -14,28 +14,30 @@ const (
|
||||
|
||||
// SystemMenu 系统菜单表 yz_system_menu
|
||||
type SystemMenu struct {
|
||||
ID uint64 `orm:"column(id);pk;auto" json:"id"` // 菜单ID
|
||||
Pid int64 `orm:"column(pid);default(0)" json:"pid"` // 上级菜单ID
|
||||
Title string `orm:"column(title);size(50)" json:"title"` // 菜单名称
|
||||
Path *string `orm:"column(path);size(200);null" json:"path"` // 路由路径
|
||||
ComponentPath *string `orm:"column(component_path);size(255);null" json:"componentPath"` // 组件路径
|
||||
Icon *string `orm:"column(icon);size(100);null" json:"icon"` // 菜单图标
|
||||
Sort int64 `orm:"column(sort);default(0)" json:"sort"` // 排序号
|
||||
Status int8 `orm:"column(status);default(0)" json:"status"` // 状态:1-启用,0-禁用
|
||||
IsVisible *int8 `orm:"column(is_visible);null" json:"isVisible"` // 是否显示:1-显示 0-不显示
|
||||
Views *string `orm:"column(views);size(255);null" json:"views"` // 菜单显示端(JSON数组字符串):[1]=平台端 [2]=租户端 [1,2]=双端
|
||||
Platforms *string `orm:"column(platforms);size(64);null" json:"platforms"` // 展示端(JSON数组字符串):["web"] ["app"] ["web","app"];空/NULL=全端
|
||||
Type int8 `orm:"column(type)" json:"type"` // 菜单类型:1-目录,2-页面,3-接口
|
||||
Permission *string `orm:"column(permission);size(100);null" json:"permission"` // 权限标识(按钮类型时填写)
|
||||
Creater *string `orm:"column(creater);size(50);null" json:"creater"` // 创建者
|
||||
Remark *string `orm:"column(remark);size(500);null" json:"remark"` // 备注
|
||||
CreateTime time.Time `orm:"column(create_time);auto_now_add;type(datetime)" json:"createTime"` // 创建时间
|
||||
UpdateTime *time.Time `orm:"column(update_time);auto_now;type(datetime);null" json:"updateTime"` // 更新时间
|
||||
DeleteTime *time.Time `orm:"column(delete_time);type(datetime);null" json:"deleteTime"` // 删除时间
|
||||
ID uint64 `orm:"column(id);pk;auto" json:"id"` // 菜单ID
|
||||
Pid int64 `orm:"column(pid);default(0)" json:"pid"` // 上级菜单ID
|
||||
Title string `orm:"column(title);size(50)" json:"title"` // 菜单名称
|
||||
Path *string `orm:"column(path);size(200);null" json:"path"` // 路由路径
|
||||
ComponentPath *string `orm:"column(component_path);size(255);null" json:"componentPath"` // 组件路径
|
||||
Icon *string `orm:"column(icon);size(100);null" json:"icon"` // 菜单图标
|
||||
Sort int64 `orm:"column(sort);default(0)" json:"sort"` // 排序号
|
||||
Status int8 `orm:"column(status);default(0)" json:"status"` // 状态:1-启用,0-禁用
|
||||
IsVisible *int8 `orm:"column(is_visible);null" json:"isVisible"` // 是否显示:1-显示 0-不显示
|
||||
// 是否必须登录才能使用:1=必须登录(默认) 0=可免登录(游客持游客令牌可用)。
|
||||
// 移动端免登录控制从模块级下沉到菜单级:公开菜单接口 /api/menus/public 只下发 0 的菜单。
|
||||
RequireLogin int8 `orm:"column(require_login);default(1)" json:"require_login"`
|
||||
Views *string `orm:"column(views);size(255);null" json:"views"` // 菜单显示端(JSON数组字符串):[1]=平台端 [2]=租户端 [1,2]=双端
|
||||
Platforms *string `orm:"column(platforms);size(64);null" json:"platforms"` // 展示端(JSON数组字符串):["web"] ["app"] ["web","app"];空/NULL=全端
|
||||
Type int8 `orm:"column(type)" json:"type"` // 菜单类型:1-目录,2-页面,3-接口
|
||||
Permission *string `orm:"column(permission);size(100);null" json:"permission"` // 权限标识(按钮类型时填写)
|
||||
Creater *string `orm:"column(creater);size(50);null" json:"creater"` // 创建者
|
||||
Remark *string `orm:"column(remark);size(500);null" json:"remark"` // 备注
|
||||
CreateTime time.Time `orm:"column(create_time);auto_now_add;type(datetime)" json:"createTime"` // 创建时间
|
||||
UpdateTime *time.Time `orm:"column(update_time);auto_now;type(datetime);null" json:"updateTime"` // 更新时间
|
||||
DeleteTime *time.Time `orm:"column(delete_time);type(datetime);null" json:"deleteTime"` // 删除时间
|
||||
}
|
||||
|
||||
// TableName 自定义表名
|
||||
func (m *SystemMenu) TableName() string {
|
||||
return "yz_system_menu"
|
||||
}
|
||||
|
||||
+31
-28
@@ -1,28 +1,31 @@
|
||||
package models
|
||||
|
||||
import "time"
|
||||
|
||||
// SystemModules 系统模块表 yz_system_modules
|
||||
type SystemModules struct {
|
||||
ID uint64 `orm:"column(id);pk;auto" json:"id"`
|
||||
Mid *uint64 `orm:"column(mid);null" json:"mid"`
|
||||
Name string `orm:"column(name);size(50)" json:"name"`
|
||||
Code string `orm:"column(code);size(50)" json:"code"`
|
||||
Path string `orm:"column(path);size(100)" json:"path"`
|
||||
Icon string `orm:"column(icon);size(50)" json:"icon"`
|
||||
Description string `orm:"column(description);size(255)" json:"description"`
|
||||
Type int `orm:"column(type);default(0)" json:"type"` // 0未分类 1功能模块 2系统配置
|
||||
Sort int `orm:"column(sort);default(0)" json:"sort"`
|
||||
Status int8 `orm:"column(status);default(1)" json:"status"` // 0禁用 1启用
|
||||
IsShow int8 `orm:"column(is_show);default(1)" json:"is_show"` // 0否 1是
|
||||
// 展示端(JSON 数组字符串):["web"] ["app"] ["web","app"];空/NULL=全端显示。
|
||||
// 取值见 PlatformWeb / PlatformApp。菜单未单独配置时继承所属模块的展示端。
|
||||
Platforms *string `orm:"column(platforms);size(64);null" json:"platforms"`
|
||||
CreateTime *time.Time `orm:"column(create_time);type(datetime);null" 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 *SystemModules) TableName() string {
|
||||
return "yz_system_modules"
|
||||
}
|
||||
package models
|
||||
|
||||
import "time"
|
||||
|
||||
// SystemModules 系统模块表 yz_system_modules
|
||||
type SystemModules struct {
|
||||
ID uint64 `orm:"column(id);pk;auto" json:"id"`
|
||||
Mid *uint64 `orm:"column(mid);null" json:"mid"`
|
||||
Name string `orm:"column(name);size(50)" json:"name"`
|
||||
Code string `orm:"column(code);size(50)" json:"code"`
|
||||
Path string `orm:"column(path);size(100)" json:"path"`
|
||||
Icon string `orm:"column(icon);size(50)" json:"icon"`
|
||||
Description string `orm:"column(description);size(255)" json:"description"`
|
||||
Type int `orm:"column(type);default(0)" json:"type"` // 0未分类 1功能模块 2系统配置
|
||||
Sort int `orm:"column(sort);default(0)" json:"sort"`
|
||||
Status int8 `orm:"column(status);default(1)" json:"status"` // 0禁用 1启用
|
||||
IsShow int8 `orm:"column(is_show);default(1)" json:"is_show"` // 0否 1是
|
||||
// 是否必须登录才能进入该模块:1=必须登录(默认,保持既有行为) 0=可免登录使用。
|
||||
// 移动端 nav 未登录时据此决定宫格卡片点击是「进登录引导」还是「直接进入」。
|
||||
RequireLogin int8 `orm:"column(require_login);default(1)" json:"require_login"`
|
||||
// 展示端(JSON 数组字符串):["web"] ["app"] ["web","app"];空/NULL=全端显示。
|
||||
// 取值见 PlatformWeb / PlatformApp。菜单未单独配置时继承所属模块的展示端。
|
||||
Platforms *string `orm:"column(platforms);size(64);null" json:"platforms"`
|
||||
CreateTime *time.Time `orm:"column(create_time);type(datetime);null" 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 *SystemModules) TableName() string {
|
||||
return "yz_system_modules"
|
||||
}
|
||||
@@ -24,6 +24,9 @@ const (
|
||||
EndPlatform End = "platform" // 平台端 /platform/**
|
||||
EndBackend End = "backend" // 租户(用户)端 /backend/**
|
||||
EndApp End = "app" // 移动端与小程序 /api/**
|
||||
// EndGuest 游客轻身份:小程序手机号授权签发的临时令牌(user_type=guest),
|
||||
// 仅免登录业务控制器显式放行,数据落在 yz_guest_data,转正后按手机号合并。
|
||||
EndGuest End = "guest"
|
||||
)
|
||||
|
||||
// 错误分类直接决定 HTTP 状态码,因此必须与前端拦截器行为对齐:
|
||||
@@ -93,6 +96,12 @@ func Claims(auth string, allowed ...End) (*jwtutil.Claims, error) {
|
||||
return claims, nil
|
||||
}
|
||||
|
||||
// IsGuest 判断解析结果是否为游客令牌(user_type=guest)。
|
||||
// 控制器据此分流:游客请求不进业务表,走 gueststore 通用存储。
|
||||
func IsGuest(claims *jwtutil.Claims) bool {
|
||||
return claims != nil && claims.UserType == string(EndGuest)
|
||||
}
|
||||
|
||||
// PathEnd 按路由前缀判定接口属于哪个端;前缀不属于任何端时 ok=false。
|
||||
func PathEnd(path string) (End, bool) {
|
||||
p := strings.ToLower(path)
|
||||
|
||||
+188
-175
@@ -1,175 +1,188 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"server/controllers"
|
||||
|
||||
beego "github.com/beego/beego/v2/server/web"
|
||||
)
|
||||
|
||||
// Register 注册移动端 / 客户端开放 API(api)路由。
|
||||
// 说明:/api 统一承载「所有移动端接口」(uniapp)与「登录器等客户端开放接口」,
|
||||
// 与 /platform、/backend、/index、/auth 各端完全隔离,不复用其它端的功能接口。
|
||||
//
|
||||
// 注意:字面量路由必须注册在 :id 通配之前(beego 按注册顺序匹配)。
|
||||
func Register() {
|
||||
RegisterErpRoutes()
|
||||
|
||||
// ===== 客户端登录器 / 开放接口(无需登录)=====
|
||||
// 客户端检查更新
|
||||
beego.Router("/api/softwareupgrade/check", &controllers.ApiSoftwareUpgradeController{}, "get:Check")
|
||||
|
||||
// 登录器上报 Cursor 设备信息
|
||||
beego.Router("/api/cursor/equipment/report", &controllers.ApiCursorEquipmentController{}, "post:Report")
|
||||
// 登录器使用激活码激活/续期 Cursor 设备
|
||||
beego.Router("/api/cursor/equipment/activateByCode", &controllers.ApiCursorEquipmentController{}, "post:ActivateByCode")
|
||||
// 登录器心跳接口,用于更新在线状态
|
||||
beego.Router("/api/cursor/equipment/heartbeat", &controllers.ApiCursorEquipmentController{}, "post:Heartbeat")
|
||||
|
||||
// Cursor Token 顺序读取/检测接口(peek 不改变号池状态)
|
||||
// GET /api/cursor/token/peek?id=11&data_type=tk
|
||||
beego.Router("/api/cursor/token/peek", &controllers.ApiCursorDetectController{}, "get:PeekToken")
|
||||
// Cursor Token 可用状态标记接口
|
||||
// POST /api/cursor/token/available?id=11
|
||||
// POST /api/cursor/token/unavailable?id=11
|
||||
beego.Router("/api/cursor/token/available", &controllers.ApiCursorDetectController{}, "post:MarkTokenAvailable")
|
||||
beego.Router("/api/cursor/token/unavailable", &controllers.ApiCursorDetectController{}, "post:MarkTokenUnavailable")
|
||||
|
||||
// 对外提卡接口
|
||||
// GET /api/getcard?type=xianyu&module=cursor&data_type=tk
|
||||
beego.Router("/api/getcard", &controllers.ApiGetCardController{}, "get:GetCard")
|
||||
|
||||
// 日程提醒确认接口
|
||||
beego.Router("/api/schedule/reminder/ack", &controllers.ApiReminderController{}, "get:AckReminder")
|
||||
|
||||
// 小程序订阅消息:取模板 / 上报授权 / 测试发送
|
||||
beego.Router("/api/mini/subscribe/templates", &controllers.AppMiniSubscribeController{}, "get:Templates")
|
||||
beego.Router("/api/mini/subscribe/authorize", &controllers.AppMiniSubscribeController{}, "post:Authorize")
|
||||
beego.Router("/api/mini/subscribe/test", &controllers.AppMiniSubscribeController{}, "post:TestNotice")
|
||||
|
||||
// ===== 移动端(uniapp)登录相关(AppAuthController)=====
|
||||
beego.Router("/api/login", &controllers.AppAuthController{}, "post:LoginBackend")
|
||||
beego.Router("/api/sendLoginCode", &controllers.AppAuthController{}, "post:SendLoginCode")
|
||||
beego.Router("/api/loginBySms", &controllers.AppAuthController{}, "post:LoginBySms")
|
||||
beego.Router("/api/logout", &controllers.AppAuthController{}, "post:Logout")
|
||||
|
||||
// 当前用户信息
|
||||
beego.Router("/api/currentUser", &controllers.AppAuthController{}, "get:GetCurrentUser")
|
||||
|
||||
// 极验与登录验证配置
|
||||
beego.Router("/api/login/getGeetest3Infos", &controllers.AppAuthController{}, "get:GetGeetest3Infos")
|
||||
beego.Router("/api/login/getGeetest4Infos", &controllers.AppAuthController{}, "get:GetGeetest4Infos")
|
||||
beego.Router("/api/login/getOpenVerify", &controllers.AppAuthController{}, "get:GetOpenVerify")
|
||||
|
||||
// 找回密码(三步流程)
|
||||
beego.Router("/api/verifyAccount", &controllers.AppAuthController{}, "post:VerifyAccount")
|
||||
beego.Router("/api/sendResetCode", &controllers.AppAuthController{}, "post:SendResetCode")
|
||||
beego.Router("/api/resetPassword", &controllers.AppAuthController{}, "post:ResetPassword")
|
||||
|
||||
// 注册
|
||||
beego.Router("/api/register", &controllers.AppAuthController{}, "post:Register")
|
||||
beego.Router("/api/sendRegisterCode", &controllers.AppAuthController{}, "post:SendRegisterCode")
|
||||
|
||||
// 日程提醒
|
||||
beego.Router("/api/schedule/list", &controllers.AppReminderController{}, "get:GetList")
|
||||
beego.Router("/api/schedule", &controllers.AppReminderController{}, "post:Create")
|
||||
beego.Router("/api/schedule/:id", &controllers.AppReminderController{}, "get:GetDetail;put:Update;delete:Delete")
|
||||
beego.Router("/api/schedule/:id/toggle", &controllers.AppReminderController{}, "post:ToggleComplete")
|
||||
|
||||
// 账号管理(密码库,与 /backend/passwordStore/* 同一套数据;字面量路由在 :id 通配之前)
|
||||
beego.Router("/api/passwordStore/list", &controllers.AppPasswordStoreController{}, "get:GetList")
|
||||
beego.Router("/api/passwordStore", &controllers.AppPasswordStoreController{}, "post:Create")
|
||||
beego.Router("/api/passwordStore/:id", &controllers.AppPasswordStoreController{}, "get:GetDetail;post:Update;delete:Delete")
|
||||
|
||||
// 记事本
|
||||
beego.Router("/api/notebook/list", &controllers.AppNotebookController{}, "get:GetList")
|
||||
beego.Router("/api/notebook", &controllers.AppNotebookController{}, "post:Create")
|
||||
beego.Router("/api/notebook/:id", &controllers.AppNotebookController{}, "get:GetDetail;put:Update;delete:Delete")
|
||||
beego.Router("/api/notebook/:id/togglePin", &controllers.AppNotebookController{}, "post:TogglePin")
|
||||
|
||||
// OA 协同办公(移动端只读:通知公告 / 文档库 / 薪酬工资单 / 员工档案)
|
||||
beego.Router("/api/oa/notice/list", &controllers.AppOaController{}, "get:NoticeList")
|
||||
beego.Router("/api/oa/notice/:id", &controllers.AppOaController{}, "get:NoticeDetail")
|
||||
|
||||
beego.Router("/api/oa/document/categories", &controllers.AppOaController{}, "get:DocCategories")
|
||||
beego.Router("/api/oa/document/list", &controllers.AppOaController{}, "get:DocList")
|
||||
beego.Router("/api/oa/document/download/:id", &controllers.AppOaController{}, "get:DocDownload")
|
||||
beego.Router("/api/oa/document/:id/star", &controllers.AppOaController{}, "post:DocStar")
|
||||
beego.Router("/api/oa/document/:id", &controllers.AppOaController{}, "get:DocDetail")
|
||||
|
||||
beego.Router("/api/oa/compensation/payrolls", &controllers.AppOaController{}, "get:PayrollList")
|
||||
beego.Router("/api/oa/compensation/payrolls/:id", &controllers.AppOaController{}, "get:PayrollDetail")
|
||||
|
||||
beego.Router("/api/oa/employee-file", &controllers.AppOaController{}, "get:MyEmployeeFile")
|
||||
// 员工档案 HR 视角:access 判定是否可看全公司档案,list 全公司档案列表,detail 档案详情
|
||||
beego.Router("/api/oa/employee-file/access", &controllers.AppOaController{}, "get:EmployeeFileAccess")
|
||||
beego.Router("/api/oa/employee-file/list", &controllers.AppOaController{}, "get:EmployeeFileList")
|
||||
beego.Router("/api/oa/employee-file/detail/:id", &controllers.AppOaController{}, "get:EmployeeFileDetail")
|
||||
|
||||
// OA 工作台仪表盘(移动端首屏顶部聚合:待办/报销统计 + 通知公告 + 今日日程)
|
||||
beego.Router("/api/oa/dashboard", &controllers.AppOaController{}, "get:Dashboard")
|
||||
|
||||
// OA 工作日程(日历待办,与 /backend/oa/schedule/* 同一套数据;小程序「工作日程」页面使用)
|
||||
beego.Router("/api/oa/schedule/list", &controllers.AppOaController{}, "get:ScheduleList")
|
||||
beego.Router("/api/oa/schedule/detail/:id", &controllers.AppOaController{}, "get:ScheduleDetail")
|
||||
beego.Router("/api/oa/schedule/create", &controllers.AppOaController{}, "post:ScheduleCreate")
|
||||
beego.Router("/api/oa/schedule/update/:id", &controllers.AppOaController{}, "post:ScheduleUpdate")
|
||||
beego.Router("/api/oa/schedule/delete/:id", &controllers.AppOaController{}, "delete:ScheduleDelete")
|
||||
beego.Router("/api/oa/schedule/finish/:id", &controllers.AppOaController{}, "post:ScheduleFinish")
|
||||
beego.Router("/api/oa/schedule/upload", &controllers.AppOaController{}, "post:ScheduleUploadImage")
|
||||
beego.Router("/api/oa/schedule/images/:id", &controllers.AppOaController{}, "post:ScheduleImages")
|
||||
|
||||
// OA 工作日报(与 /backend/oa/dailyreport/* 同一套数据;小程序「工作日报」页面使用)
|
||||
beego.Router("/api/oa/dailyreport/profile", &controllers.AppOaController{}, "get:DailyReportProfile")
|
||||
beego.Router("/api/oa/dailyreport/mine/list", &controllers.AppOaController{}, "get:DailyReportMineList")
|
||||
beego.Router("/api/oa/dailyreport/calendar", &controllers.AppOaController{}, "get:DailyReportCalendar")
|
||||
beego.Router("/api/oa/dailyreport/save", &controllers.AppOaController{}, "post:DailyReportSave")
|
||||
beego.Router("/api/oa/dailyreport/withdraw/:id", &controllers.AppOaController{}, "post:DailyReportWithdraw")
|
||||
beego.Router("/api/oa/dailyreport/detail/:id", &controllers.AppOaController{}, "get:DailyReportDetail")
|
||||
beego.Router("/api/oa/dailyreport/delete/:id", &controllers.AppOaController{}, "delete:DailyReportDelete")
|
||||
beego.Router("/api/oa/dailyreport/team/status", &controllers.AppOaController{}, "get:DailyReportTeamStatus")
|
||||
beego.Router("/api/oa/dailyreport/team/orgs", &controllers.AppOaController{}, "get:DailyReportTeamOrgs")
|
||||
beego.Router("/api/oa/dailyreport/comment", &controllers.AppOaController{}, "post:DailyReportComment")
|
||||
beego.Router("/api/oa/dailyreport/comment/:id", &controllers.AppOaController{}, "delete:DailyReportCommentDelete")
|
||||
beego.Router("/api/oa/dailyreport/upload", &controllers.AppOaController{}, "post:DailyReportUpload")
|
||||
|
||||
// OA 报销管理(与 /backend/reimbursements* 同一套数据;小程序「报销管理」页面使用)
|
||||
beego.Router("/api/oa/expense/list", &controllers.AppOaController{}, "get:ExpenseList")
|
||||
beego.Router("/api/oa/expense/types", &controllers.AppOaController{}, "get:ExpenseTypes")
|
||||
beego.Router("/api/oa/expense/upload", &controllers.AppOaController{}, "post:ExpenseUploadImage")
|
||||
beego.Router("/api/oa/expense/detail/:id", &controllers.AppOaController{}, "get:ExpenseDetail")
|
||||
beego.Router("/api/oa/expense/create", &controllers.AppOaController{}, "post:ExpenseCreate")
|
||||
beego.Router("/api/oa/expense/update/:id", &controllers.AppOaController{}, "post:ExpenseUpdate")
|
||||
beego.Router("/api/oa/expense/delete/:id", &controllers.AppOaController{}, "delete:ExpenseDelete")
|
||||
beego.Router("/api/oa/expense/item/add/:id", &controllers.AppOaController{}, "post:ExpenseAddItem")
|
||||
beego.Router("/api/oa/expense/item/update/:id/:item_id", &controllers.AppOaController{}, "post:ExpenseUpdateItem")
|
||||
beego.Router("/api/oa/expense/item/delete/:id/:item_id", &controllers.AppOaController{}, "delete:ExpenseDeleteItem")
|
||||
beego.Router("/api/oa/expense/submit/:id", &controllers.AppOaController{}, "post:ExpenseSubmit")
|
||||
beego.Router("/api/oa/expense/approve/:id", &controllers.AppOaController{}, "post:ExpenseApprove")
|
||||
beego.Router("/api/oa/expense/reject/:id", &controllers.AppOaController{}, "post:ExpenseReject")
|
||||
beego.Router("/api/oa/expense/my-approve-task/:id", &controllers.AppOaController{}, "get:ExpenseMyApproveTask")
|
||||
beego.Router("/api/oa/expense/status/:id", &controllers.AppOaController{}, "post:ExpenseStatus")
|
||||
beego.Router("/api/oa/expense/records/:id", &controllers.AppOaController{}, "get:ExpenseRecords")
|
||||
|
||||
// 通讯录(移动端只读:列表 / 组织树 / 详情)
|
||||
beego.Router("/api/oa/contact/list", &controllers.AppOaController{}, "get:ContactList")
|
||||
beego.Router("/api/oa/contact/org-tree", &controllers.AppOaController{}, "get:ContactOrgTree")
|
||||
beego.Router("/api/oa/contact/:id", &controllers.AppOaController{}, "get:ContactDetail")
|
||||
|
||||
// 自定义审批流(移动端:我发起的 / 待我审批 / 详情 / 提交 / 通过驳回 / 撤回)
|
||||
beego.Router("/api/oa/approval/categories", &controllers.AppOaController{}, "get:ApprovalCategories")
|
||||
beego.Router("/api/oa/approval/flows", &controllers.AppOaController{}, "get:ApprovalFlows")
|
||||
beego.Router("/api/oa/approval/list", &controllers.AppOaController{}, "get:ApprovalList")
|
||||
beego.Router("/api/oa/approval/todo", &controllers.AppOaController{}, "get:ApprovalTodo")
|
||||
beego.Router("/api/oa/approval/submit", &controllers.AppOaController{}, "post:ApprovalSubmit")
|
||||
beego.Router("/api/oa/approval/approve", &controllers.AppOaController{}, "post:ApprovalApprove")
|
||||
beego.Router("/api/oa/approval/withdraw/:id", &controllers.AppOaController{}, "post:ApprovalWithdraw")
|
||||
beego.Router("/api/oa/approval/detail/:id", &controllers.AppOaController{}, "get:ApprovalDetail")
|
||||
|
||||
// 活动日志
|
||||
beego.Router("/api/activity/list", &controllers.AppActivityController{}, "get:GetList")
|
||||
|
||||
// 工作台聚合(ERP/OA/CRM/CMS 应用门户首屏数据)
|
||||
beego.Router("/api/home", &controllers.AppAggregateController{}, "get:Home")
|
||||
// 移动端用户自定义「常用功能」保存(读取随 /api/home 下发 quick_menu_ids)
|
||||
beego.Router("/api/quick-menus", &controllers.AppAggregateController{}, "post:SaveQuickMenus")
|
||||
}
|
||||
package api
|
||||
|
||||
import (
|
||||
"server/controllers"
|
||||
|
||||
beego "github.com/beego/beego/v2/server/web"
|
||||
)
|
||||
|
||||
// Register 注册移动端 / 客户端开放 API(api)路由。
|
||||
// 说明:/api 统一承载「所有移动端接口」(uniapp)与「登录器等客户端开放接口」,
|
||||
// 与 /platform、/backend、/index、/auth 各端完全隔离,不复用其它端的功能接口。
|
||||
//
|
||||
// 注意:字面量路由必须注册在 :id 通配之前(beego 按注册顺序匹配)。
|
||||
func Register() {
|
||||
RegisterErpRoutes()
|
||||
|
||||
// ===== 客户端登录器 / 开放接口(无需登录)=====
|
||||
// 客户端检查更新
|
||||
beego.Router("/api/softwareupgrade/check", &controllers.ApiSoftwareUpgradeController{}, "get:Check")
|
||||
|
||||
// 登录器上报 Cursor 设备信息
|
||||
beego.Router("/api/cursor/equipment/report", &controllers.ApiCursorEquipmentController{}, "post:Report")
|
||||
// 登录器使用激活码激活/续期 Cursor 设备
|
||||
beego.Router("/api/cursor/equipment/activateByCode", &controllers.ApiCursorEquipmentController{}, "post:ActivateByCode")
|
||||
// 登录器心跳接口,用于更新在线状态
|
||||
beego.Router("/api/cursor/equipment/heartbeat", &controllers.ApiCursorEquipmentController{}, "post:Heartbeat")
|
||||
|
||||
// Cursor Token 顺序读取/检测接口(peek 不改变号池状态)
|
||||
// GET /api/cursor/token/peek?id=11&data_type=tk
|
||||
beego.Router("/api/cursor/token/peek", &controllers.ApiCursorDetectController{}, "get:PeekToken")
|
||||
// Cursor Token 可用状态标记接口
|
||||
// POST /api/cursor/token/available?id=11
|
||||
// POST /api/cursor/token/unavailable?id=11
|
||||
beego.Router("/api/cursor/token/available", &controllers.ApiCursorDetectController{}, "post:MarkTokenAvailable")
|
||||
beego.Router("/api/cursor/token/unavailable", &controllers.ApiCursorDetectController{}, "post:MarkTokenUnavailable")
|
||||
|
||||
// 对外提卡接口
|
||||
// GET /api/getcard?type=xianyu&module=cursor&data_type=tk
|
||||
beego.Router("/api/getcard", &controllers.ApiGetCardController{}, "get:GetCard")
|
||||
|
||||
// 日程提醒确认接口
|
||||
beego.Router("/api/schedule/reminder/ack", &controllers.ApiReminderController{}, "get:AckReminder")
|
||||
|
||||
// 小程序订阅消息:取模板 / 上报授权 / 测试发送
|
||||
beego.Router("/api/mini/subscribe/templates", &controllers.AppMiniSubscribeController{}, "get:Templates")
|
||||
beego.Router("/api/mini/subscribe/authorize", &controllers.AppMiniSubscribeController{}, "post:Authorize")
|
||||
beego.Router("/api/mini/subscribe/test", &controllers.AppMiniSubscribeController{}, "post:TestNotice")
|
||||
|
||||
// ===== 移动端(uniapp)登录相关(AppAuthController)=====
|
||||
beego.Router("/api/login", &controllers.AppAuthController{}, "post:LoginBackend")
|
||||
beego.Router("/api/sendLoginCode", &controllers.AppAuthController{}, "post:SendLoginCode")
|
||||
beego.Router("/api/loginBySms", &controllers.AppAuthController{}, "post:LoginBySms")
|
||||
beego.Router("/api/logout", &controllers.AppAuthController{}, "post:Logout")
|
||||
|
||||
// 当前用户信息
|
||||
beego.Router("/api/currentUser", &controllers.AppAuthController{}, "get:GetCurrentUser")
|
||||
|
||||
// 极验与登录验证配置
|
||||
beego.Router("/api/login/getGeetest3Infos", &controllers.AppAuthController{}, "get:GetGeetest3Infos")
|
||||
beego.Router("/api/login/getGeetest4Infos", &controllers.AppAuthController{}, "get:GetGeetest4Infos")
|
||||
beego.Router("/api/login/getOpenVerify", &controllers.AppAuthController{}, "get:GetOpenVerify")
|
||||
|
||||
// 找回密码(三步流程)
|
||||
beego.Router("/api/verifyAccount", &controllers.AppAuthController{}, "post:VerifyAccount")
|
||||
beego.Router("/api/sendResetCode", &controllers.AppAuthController{}, "post:SendResetCode")
|
||||
beego.Router("/api/resetPassword", &controllers.AppAuthController{}, "post:ResetPassword")
|
||||
|
||||
// 注册
|
||||
beego.Router("/api/register", &controllers.AppAuthController{}, "post:Register")
|
||||
beego.Router("/api/sendRegisterCode", &controllers.AppAuthController{}, "post:SendRegisterCode")
|
||||
|
||||
// 日程提醒
|
||||
beego.Router("/api/schedule/list", &controllers.AppReminderController{}, "get:GetList")
|
||||
beego.Router("/api/schedule", &controllers.AppReminderController{}, "post:Create")
|
||||
beego.Router("/api/schedule/:id", &controllers.AppReminderController{}, "get:GetDetail;put:Update;delete:Delete")
|
||||
beego.Router("/api/schedule/:id/toggle", &controllers.AppReminderController{}, "post:ToggleComplete")
|
||||
|
||||
// 账号管理(密码库,与 /backend/passwordStore/* 同一套数据;字面量路由在 :id 通配之前)
|
||||
beego.Router("/api/passwordStore/list", &controllers.AppPasswordStoreController{}, "get:GetList")
|
||||
beego.Router("/api/passwordStore", &controllers.AppPasswordStoreController{}, "post:Create")
|
||||
beego.Router("/api/passwordStore/:id", &controllers.AppPasswordStoreController{}, "get:GetDetail;post:Update;delete:Delete")
|
||||
|
||||
// 记事本
|
||||
beego.Router("/api/notebook/list", &controllers.AppNotebookController{}, "get:GetList")
|
||||
beego.Router("/api/notebook", &controllers.AppNotebookController{}, "post:Create")
|
||||
beego.Router("/api/notebook/:id", &controllers.AppNotebookController{}, "get:GetDetail;put:Update;delete:Delete")
|
||||
beego.Router("/api/notebook/:id/togglePin", &controllers.AppNotebookController{}, "post:TogglePin")
|
||||
|
||||
// OA 协同办公(移动端只读:通知公告 / 文档库 / 薪酬工资单 / 员工档案)
|
||||
beego.Router("/api/oa/notice/list", &controllers.AppOaController{}, "get:NoticeList")
|
||||
beego.Router("/api/oa/notice/:id", &controllers.AppOaController{}, "get:NoticeDetail")
|
||||
|
||||
beego.Router("/api/oa/document/categories", &controllers.AppOaController{}, "get:DocCategories")
|
||||
beego.Router("/api/oa/document/list", &controllers.AppOaController{}, "get:DocList")
|
||||
beego.Router("/api/oa/document/download/:id", &controllers.AppOaController{}, "get:DocDownload")
|
||||
beego.Router("/api/oa/document/:id/star", &controllers.AppOaController{}, "post:DocStar")
|
||||
beego.Router("/api/oa/document/:id", &controllers.AppOaController{}, "get:DocDetail")
|
||||
|
||||
beego.Router("/api/oa/compensation/payrolls", &controllers.AppOaController{}, "get:PayrollList")
|
||||
beego.Router("/api/oa/compensation/payrolls/:id", &controllers.AppOaController{}, "get:PayrollDetail")
|
||||
|
||||
beego.Router("/api/oa/employee-file", &controllers.AppOaController{}, "get:MyEmployeeFile")
|
||||
// 员工档案 HR 视角:access 判定是否可看全公司档案,list 全公司档案列表,detail 档案详情
|
||||
beego.Router("/api/oa/employee-file/access", &controllers.AppOaController{}, "get:EmployeeFileAccess")
|
||||
beego.Router("/api/oa/employee-file/list", &controllers.AppOaController{}, "get:EmployeeFileList")
|
||||
beego.Router("/api/oa/employee-file/detail/:id", &controllers.AppOaController{}, "get:EmployeeFileDetail")
|
||||
|
||||
// OA 工作台仪表盘(移动端首屏顶部聚合:待办/报销统计 + 通知公告 + 今日日程)
|
||||
beego.Router("/api/oa/dashboard", &controllers.AppOaController{}, "get:Dashboard")
|
||||
|
||||
// OA 工作日程(日历待办,与 /backend/oa/schedule/* 同一套数据;小程序「工作日程」页面使用)
|
||||
beego.Router("/api/oa/schedule/list", &controllers.AppOaController{}, "get:ScheduleList")
|
||||
beego.Router("/api/oa/schedule/detail/:id", &controllers.AppOaController{}, "get:ScheduleDetail")
|
||||
beego.Router("/api/oa/schedule/create", &controllers.AppOaController{}, "post:ScheduleCreate")
|
||||
beego.Router("/api/oa/schedule/update/:id", &controllers.AppOaController{}, "post:ScheduleUpdate")
|
||||
beego.Router("/api/oa/schedule/delete/:id", &controllers.AppOaController{}, "delete:ScheduleDelete")
|
||||
beego.Router("/api/oa/schedule/finish/:id", &controllers.AppOaController{}, "post:ScheduleFinish")
|
||||
beego.Router("/api/oa/schedule/upload", &controllers.AppOaController{}, "post:ScheduleUploadImage")
|
||||
beego.Router("/api/oa/schedule/images/:id", &controllers.AppOaController{}, "post:ScheduleImages")
|
||||
|
||||
// OA 工作日报(与 /backend/oa/dailyreport/* 同一套数据;小程序「工作日报」页面使用)
|
||||
beego.Router("/api/oa/dailyreport/profile", &controllers.AppOaController{}, "get:DailyReportProfile")
|
||||
beego.Router("/api/oa/dailyreport/mine/list", &controllers.AppOaController{}, "get:DailyReportMineList")
|
||||
beego.Router("/api/oa/dailyreport/calendar", &controllers.AppOaController{}, "get:DailyReportCalendar")
|
||||
beego.Router("/api/oa/dailyreport/save", &controllers.AppOaController{}, "post:DailyReportSave")
|
||||
beego.Router("/api/oa/dailyreport/withdraw/:id", &controllers.AppOaController{}, "post:DailyReportWithdraw")
|
||||
beego.Router("/api/oa/dailyreport/detail/:id", &controllers.AppOaController{}, "get:DailyReportDetail")
|
||||
beego.Router("/api/oa/dailyreport/delete/:id", &controllers.AppOaController{}, "delete:DailyReportDelete")
|
||||
beego.Router("/api/oa/dailyreport/team/status", &controllers.AppOaController{}, "get:DailyReportTeamStatus")
|
||||
beego.Router("/api/oa/dailyreport/team/orgs", &controllers.AppOaController{}, "get:DailyReportTeamOrgs")
|
||||
beego.Router("/api/oa/dailyreport/comment", &controllers.AppOaController{}, "post:DailyReportComment")
|
||||
beego.Router("/api/oa/dailyreport/comment/:id", &controllers.AppOaController{}, "delete:DailyReportCommentDelete")
|
||||
beego.Router("/api/oa/dailyreport/upload", &controllers.AppOaController{}, "post:DailyReportUpload")
|
||||
|
||||
// OA 报销管理(与 /backend/reimbursements* 同一套数据;小程序「报销管理」页面使用)
|
||||
beego.Router("/api/oa/expense/list", &controllers.AppOaController{}, "get:ExpenseList")
|
||||
beego.Router("/api/oa/expense/types", &controllers.AppOaController{}, "get:ExpenseTypes")
|
||||
beego.Router("/api/oa/expense/upload", &controllers.AppOaController{}, "post:ExpenseUploadImage")
|
||||
beego.Router("/api/oa/expense/detail/:id", &controllers.AppOaController{}, "get:ExpenseDetail")
|
||||
beego.Router("/api/oa/expense/create", &controllers.AppOaController{}, "post:ExpenseCreate")
|
||||
beego.Router("/api/oa/expense/update/:id", &controllers.AppOaController{}, "post:ExpenseUpdate")
|
||||
beego.Router("/api/oa/expense/delete/:id", &controllers.AppOaController{}, "delete:ExpenseDelete")
|
||||
beego.Router("/api/oa/expense/item/add/:id", &controllers.AppOaController{}, "post:ExpenseAddItem")
|
||||
beego.Router("/api/oa/expense/item/update/:id/:item_id", &controllers.AppOaController{}, "post:ExpenseUpdateItem")
|
||||
beego.Router("/api/oa/expense/item/delete/:id/:item_id", &controllers.AppOaController{}, "delete:ExpenseDeleteItem")
|
||||
beego.Router("/api/oa/expense/submit/:id", &controllers.AppOaController{}, "post:ExpenseSubmit")
|
||||
beego.Router("/api/oa/expense/approve/:id", &controllers.AppOaController{}, "post:ExpenseApprove")
|
||||
beego.Router("/api/oa/expense/reject/:id", &controllers.AppOaController{}, "post:ExpenseReject")
|
||||
beego.Router("/api/oa/expense/my-approve-task/:id", &controllers.AppOaController{}, "get:ExpenseMyApproveTask")
|
||||
beego.Router("/api/oa/expense/status/:id", &controllers.AppOaController{}, "post:ExpenseStatus")
|
||||
beego.Router("/api/oa/expense/records/:id", &controllers.AppOaController{}, "get:ExpenseRecords")
|
||||
|
||||
// 通讯录(移动端只读:列表 / 组织树 / 详情)
|
||||
beego.Router("/api/oa/contact/list", &controllers.AppOaController{}, "get:ContactList")
|
||||
beego.Router("/api/oa/contact/org-tree", &controllers.AppOaController{}, "get:ContactOrgTree")
|
||||
beego.Router("/api/oa/contact/:id", &controllers.AppOaController{}, "get:ContactDetail")
|
||||
|
||||
// 自定义审批流(移动端:我发起的 / 待我审批 / 详情 / 提交 / 通过驳回 / 撤回)
|
||||
beego.Router("/api/oa/approval/categories", &controllers.AppOaController{}, "get:ApprovalCategories")
|
||||
beego.Router("/api/oa/approval/flows", &controllers.AppOaController{}, "get:ApprovalFlows")
|
||||
beego.Router("/api/oa/approval/list", &controllers.AppOaController{}, "get:ApprovalList")
|
||||
beego.Router("/api/oa/approval/todo", &controllers.AppOaController{}, "get:ApprovalTodo")
|
||||
beego.Router("/api/oa/approval/submit", &controllers.AppOaController{}, "post:ApprovalSubmit")
|
||||
beego.Router("/api/oa/approval/approve", &controllers.AppOaController{}, "post:ApprovalApprove")
|
||||
beego.Router("/api/oa/approval/withdraw/:id", &controllers.AppOaController{}, "post:ApprovalWithdraw")
|
||||
beego.Router("/api/oa/approval/detail/:id", &controllers.AppOaController{}, "get:ApprovalDetail")
|
||||
|
||||
// 活动日志
|
||||
beego.Router("/api/activity/list", &controllers.AppActivityController{}, "get:GetList")
|
||||
|
||||
// 工作台聚合(ERP/OA/CRM/CMS 应用门户首屏数据)
|
||||
beego.Router("/api/home", &controllers.AppAggregateController{}, "get:Home")
|
||||
// 移动端未登录时的模块宫格(公开:返回模块列表 + require_login 标记,路径不在鉴权前缀内)
|
||||
beego.Router("/api/modules/public", &controllers.AppAggregateController{}, "get:PublicModules")
|
||||
// 移动端免登录菜单(公开:菜单级 require_login=0 的集合,游客可用功能的唯一入口清单)
|
||||
beego.Router("/api/menus/public", &controllers.AppAggregateController{}, "get:PublicMenus")
|
||||
|
||||
// ===== 游客(免登录模块)=====
|
||||
// 微信手机号授权签发游客令牌(公开接口,不在鉴权前缀内,凭证由控制器校验)
|
||||
beego.Router("/api/guest/mini-login", &controllers.GuestController{}, "post:MiniLogin")
|
||||
// 正式登录后把游客数据并入正式账号(需正式令牌)
|
||||
beego.Router("/api/guest/merge", &controllers.GuestController{}, "post:Merge")
|
||||
// 通用游客数据接口(需游客令牌,供后续免登录模块直接复用)
|
||||
beego.Router("/api/guest/data", &controllers.GuestController{}, "get:DataList;post:DataCreate")
|
||||
beego.Router("/api/guest/data/:id", &controllers.GuestController{}, "put:DataUpdate;delete:DataDelete")
|
||||
// 移动端用户自定义「常用功能」保存(读取随 /api/home 下发 quick_menu_ids)
|
||||
beego.Router("/api/quick-menus", &controllers.AppAggregateController{}, "post:SaveQuickMenus")
|
||||
}
|
||||
Reference in new issue
Block a user