158 lines
4.5 KiB
Go
158 lines
4.5 KiB
Go
package controllers
|
|
|
|
import (
|
|
"encoding/json"
|
|
"io"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"server/pkg/identity"
|
|
"server/pkg/jwtutil"
|
|
|
|
beego "github.com/beego/beego/v2/server/web"
|
|
)
|
|
|
|
// AppErpBaseController 移动端进销存接口公共底座。
|
|
//
|
|
// 与 /backend/erp/** 的关系(接口隔离规范):两端各有独立 handler 与路由,
|
|
// 但单据校验、明细装配、库存/往来账联动与报表统计的口径只能有一份实现,
|
|
// 全部在 erp_*_core.go 里;这里只负责「解移动端令牌 → 组装 erpActor → 调核心 → 输出响应」。
|
|
//
|
|
// 鉴权:/api/** 只被 uniapp 调用,放行 app 与 backend 两类令牌,平台令牌不允许打移动端接口。
|
|
// 数据隔离:tenant_id 一律取自 JWT,前端无法指定租户。
|
|
type AppErpBaseController struct {
|
|
beego.Controller
|
|
}
|
|
|
|
func (c *AppErpBaseController) erpClaims() (*jwtutil.Claims, error) {
|
|
return identity.Claims(c.Ctx.Request.Header.Get("Authorization"), identity.EndApp, identity.EndBackend)
|
|
}
|
|
|
|
// actor 组装核心逻辑需要的调用者身份
|
|
func (c *AppErpBaseController) actor(cl *jwtutil.Claims) erpActor {
|
|
return erpNewActor(cl)
|
|
}
|
|
|
|
func (c *AppErpBaseController) erpOk(data interface{}) {
|
|
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": data}
|
|
_ = c.ServeJSON()
|
|
}
|
|
|
|
// erpListOK 分页出参形状与 Web 端保持一致:{list,total,page,pageSize,...extra}
|
|
func (c *AppErpBaseController) erpListOK(list interface{}, total int64, page, pageSize int, extra map[string]interface{}) {
|
|
data := map[string]interface{}{"list": list, "total": total, "page": page, "pageSize": pageSize}
|
|
for k, v := range extra {
|
|
data[k] = v
|
|
}
|
|
c.erpOk(data)
|
|
}
|
|
|
|
func (c *AppErpBaseController) erpFail(httpStatus, bizCode int, msg string) {
|
|
c.Ctx.Output.SetStatus(httpStatus)
|
|
c.Data["json"] = map[string]interface{}{"code": bizCode, "msg": msg}
|
|
_ = c.ServeJSON()
|
|
}
|
|
|
|
// erpDenied 鉴权失败统一出口(返回 true 表示已响应,调用方应 return)
|
|
func (c *AppErpBaseController) erpDenied(err error) bool {
|
|
if err != nil {
|
|
code := identity.Status(err)
|
|
c.erpFail(code, code, err.Error())
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (c *AppErpBaseController) erpPathID() uint64 {
|
|
id, _ := strconv.ParseUint(c.Ctx.Input.Param(":id"), 10, 64)
|
|
return id
|
|
}
|
|
|
|
// erpPage 分页参数:默认 20,上限 100(与 Web 端口径一致)
|
|
func (c *AppErpBaseController) erpPage() (int, int) {
|
|
page, _ := c.GetInt("page", 1)
|
|
pageSize, _ := c.GetInt("pageSize", 20)
|
|
if page < 1 {
|
|
page = 1
|
|
}
|
|
if pageSize < 1 || pageSize > 100 {
|
|
pageSize = 20
|
|
}
|
|
return page, pageSize
|
|
}
|
|
|
|
// erpBody 解析 JSON 请求体;allowEmpty 为 true 时允许空 body(自动核销等场景)
|
|
func (c *AppErpBaseController) erpBody(v interface{}, allowEmpty bool) bool {
|
|
raw, err := io.ReadAll(c.Ctx.Request.Body)
|
|
if err != nil {
|
|
c.erpFail(400, 400, "参数错误")
|
|
return false
|
|
}
|
|
if len(strings.TrimSpace(string(raw))) == 0 {
|
|
if allowEmpty {
|
|
return true
|
|
}
|
|
c.erpFail(400, 400, "请求体为空")
|
|
return false
|
|
}
|
|
if err := json.Unmarshal(raw, v); err != nil {
|
|
c.erpFail(400, 400, "参数错误: "+err.Error())
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (c *AppErpBaseController) erpQuery(name string) string {
|
|
return strings.TrimSpace(c.GetString(name))
|
|
}
|
|
|
|
func (c *AppErpBaseController) erpUint(name string) uint64 {
|
|
return erpUintParam(c.GetString(name))
|
|
}
|
|
|
|
func (c *AppErpBaseController) erpFlag(name string) bool {
|
|
return c.erpQuery(name) == "1"
|
|
}
|
|
|
|
// erpDateRange 报表/流水的日期区间:默认当月,date_to 含当天
|
|
func (c *AppErpBaseController) erpDateRange(defMonths int) (time.Time, time.Time) {
|
|
from, to := erpMonthRange(defMonths)
|
|
if d := c.erpQuery("date_from"); d != "" {
|
|
if t, e := time.ParseInLocation("2006-01-02", d, time.Local); e == nil {
|
|
from = t
|
|
}
|
|
}
|
|
if d := c.erpQuery("date_to"); d != "" {
|
|
if t, e := time.ParseInLocation("2006-01-02", d, time.Local); e == nil {
|
|
to = t.AddDate(0, 0, 1)
|
|
}
|
|
}
|
|
return from, to
|
|
}
|
|
|
|
// ---- 经营看板 / 字典 ----
|
|
|
|
// AppErpController 移动端进销存首页聚合与全局字典。
|
|
type AppErpController struct {
|
|
AppErpBaseController
|
|
}
|
|
|
|
// Dashboard GET /api/erp/dashboard 经营看板(与 Web 端同一份统计口径)
|
|
func (c *AppErpController) Dashboard() {
|
|
cl, err := c.erpClaims()
|
|
if c.erpDenied(err) {
|
|
return
|
|
}
|
|
c.erpOk(erpDashboardCore(c.actor(cl), c.erpQuery("period")))
|
|
}
|
|
|
|
// Meta GET /api/erp/meta 单据类型、状态、收付款方式等下拉字典
|
|
func (c *AppErpController) Meta() {
|
|
cl, err := c.erpClaims()
|
|
if c.erpDenied(err) {
|
|
return
|
|
}
|
|
c.erpOk(erpDocMetaDict(c.actor(cl)))
|
|
}
|