From 6de78a5a2a77d64118af6f43c064bbb16d37078a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=AB=E5=9C=B0=E5=83=A7?= <357099073@qq.com> Date: Wed, 13 May 2026 08:37:54 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0erp?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- controllers/backend_erp.go | 1078 ++++++++++++++++++++++++++++++++++++ models/erp.go | 73 +++ models/init.go | 3 + routers/backend/backend.go | 28 + 4 files changed, 1182 insertions(+) create mode 100644 controllers/backend_erp.go create mode 100644 models/erp.go diff --git a/controllers/backend_erp.go b/controllers/backend_erp.go new file mode 100644 index 0000000..1b8b1c5 --- /dev/null +++ b/controllers/backend_erp.go @@ -0,0 +1,1078 @@ +package controllers + +import ( + "crypto/sha256" + "encoding/hex" + "encoding/json" + "strconv" + "strings" + "time" + + "server/models" + + "github.com/beego/beego/v2/client/orm" + beego "github.com/beego/beego/v2/server/web" +) + +// BackendErpController 兼容 backend 前端 /admin/erp/* 组织机构、员工、职位接口。 +type BackendErpController struct { + beego.Controller +} + +type erpOrganizationDTO struct { + ID uint64 `json:"id"` + Tid uint64 `json:"tid"` + TenantID uint64 `json:"tenant_id"` + OrgName string `json:"org_name"` + OrgCode string `json:"org_code"` + ParentID uint64 `json:"parent_id"` + ParentName string `json:"parent_name"` + LeaderID uint64 `json:"leader_id"` + LeaderName string `json:"leader_name"` + IsCompany int `json:"is_company"` + Sort uint `json:"sort"` + Status int8 `json:"status"` + Remark string `json:"remark"` +} + +type erpEmployeeDTO struct { + ID uint `json:"id"` + Tid int `json:"tid"` + TenantID int `json:"tenant_id"` + Account string `json:"account"` + Name string `json:"name"` + Gender int8 `json:"gender"` + Sex int8 `json:"sex"` + Birthday string `json:"birthday"` + AffiliateUnit string `json:"affiliate_unit"` + AffiliateUnitName string `json:"affiliate_unit_name"` + Department string `json:"department"` + DepartmentName string `json:"department_name"` + Position string `json:"position"` + Education string `json:"education"` + Nation string `json:"nation"` + Phone string `json:"phone"` + Wechat string `json:"wechat"` + Email string `json:"email"` + HomeAddress string `json:"home_address"` + AccountStatus int8 `json:"account_status"` + Status int8 `json:"status"` +} + +type erpPositionDTO struct { + ID uint64 `json:"id"` + TenantID uint64 `json:"tenant_id"` + Tid uint64 `json:"tid"` + DepartmentID uint64 `json:"department_id"` + PositionCode string `json:"position_code"` + PositionName string `json:"position_name"` + PositionType int8 `json:"position_type"` + Status int8 `json:"status"` + Sort uint `json:"sort"` +} + +// GetOrganization 获取组织机构列表。 +// GET /admin/erp/getOrganization +func (c *BackendErpController) GetOrganization() { + tid, _ := c.GetInt("tid") + + qs := models.Orm.QueryTable(new(models.BackendErpOrganization)). + Filter("delete_time__isnull", true). + Exclude("status", 0) + if tid > 0 { + qs = qs.Filter("tid", tid) + } + + var rows []models.BackendErpOrganization + _, err := qs.OrderBy("sort", "id").All(&rows) + if err != nil { + c.jsonError(500, "查询组织机构失败: "+err.Error()) + return + } + + list := make([]erpOrganizationDTO, 0, len(rows)) + for _, row := range rows { + list = append(list, c.organizationDTO(row)) + } + + c.jsonOK(list) +} + +// GetOrganizationDetail 获取组织机构详情。 +// GET /admin/erp/getOrganizationDetail/:id +func (c *BackendErpController) GetOrganizationDetail() { + id, ok := c.pathUint64(":id") + if !ok { + c.jsonError(400, "无效ID") + return + } + + var row models.BackendErpOrganization + err := models.Orm.QueryTable(new(models.BackendErpOrganization)). + Filter("id", id). + Filter("delete_time__isnull", true). + Exclude("status", 0). + One(&row) + if err != nil { + c.jsonError(404, "组织机构不存在") + return + } + + c.jsonOK(c.organizationDTO(row)) +} + +// CreateOrganization 创建组织机构。 +// POST /admin/erp/createOrganization +func (c *BackendErpController) CreateOrganization() { + body := c.parseJSONBody() + + orgName, _ := c.getStringValue(body, "org_name", "name") + orgName = strings.TrimSpace(orgName) + if orgName == "" { + c.jsonError(400, "组织名称不能为空") + return + } + + orgCode, _ := c.getStringValue(body, "org_code", "code") + orgCode = strings.TrimSpace(orgCode) + if orgCode == "" { + orgCode = "ORG" + c.nowCompactString() + } + + tid, _ := c.getUint64Value(body, "tid", "tenant_id") + parentID, _ := c.getUint64Value(body, "parent_id") + leaderID, hasLeader := c.getUint64Value(body, "leader_id") + sortVal, _ := c.getUintValue(body, "sort") + isCompany, hasCompany := c.getIntValue(body, "is_company") + status, hasStatus := c.getIntValue(body, "status") + remark, _ := c.getStringValue(body, "remark") + + row := models.BackendErpOrganization{ + Tid: tid, + OrgName: orgName, + OrgCode: orgCode, + ParentID: parentID, + Sort: sortVal, + IsCompany: boolInt(parentID == 0), + Status: 1, + Remark: strPtrIfNotEmpty(remark), + } + if hasLeader && leaderID > 0 { + row.LeaderID = &leaderID + } + if hasCompany { + row.IsCompany = isCompany + } + if hasStatus { + row.Status = int8(status) + } + + id, err := models.Orm.Insert(&row) + if err != nil { + c.jsonError(500, "创建组织机构失败: "+err.Error()) + return + } + + c.jsonOK(map[string]interface{}{"id": id}) +} + +// EditOrganization 更新组织机构。 +// POST /admin/erp/editOrganization/:id +func (c *BackendErpController) EditOrganization() { + id, ok := c.pathUint64(":id") + if !ok { + c.jsonError(400, "无效ID") + return + } + + body := c.parseJSONBody() + update := orm.Params{} + + if v, has := c.getStringValue(body, "org_name", "name"); has { + v = strings.TrimSpace(v) + if v == "" { + c.jsonError(400, "组织名称不能为空") + return + } + update["org_name"] = v + } + if v, has := c.getStringValue(body, "org_code", "code"); has { + update["org_code"] = strings.TrimSpace(v) + } + if v, has := c.getUint64Value(body, "parent_id"); has { + update["parent_id"] = v + } + if v, has := c.getUint64Value(body, "tid", "tenant_id"); has { + update["tid"] = v + } + if v, has := c.getUint64Value(body, "leader_id"); has { + update["leader_id"] = nullableUint64(v) + } + if v, has := c.getUintValue(body, "sort"); has { + update["sort"] = v + } + if v, has := c.getIntValue(body, "is_company"); has { + update["is_company"] = v + } + if v, has := c.getIntValue(body, "status"); has { + update["status"] = int8(v) + } + if v, has := c.getStringValue(body, "remark"); has { + update["remark"] = nullableString(v) + } + + if len(update) == 0 { + c.jsonError(400, "无更新字段") + return + } + + num, err := models.Orm.QueryTable(new(models.BackendErpOrganization)). + Filter("id", id). + Filter("delete_time__isnull", true). + Update(update) + if err != nil { + c.jsonError(500, "更新组织机构失败: "+err.Error()) + return + } + if num == 0 { + c.jsonError(404, "组织机构不存在") + return + } + + c.jsonOK(nil) +} + +// DeleteOrganization 删除组织机构。 +// DELETE /admin/erp/deleteOrganization/:id +func (c *BackendErpController) DeleteOrganization() { + id, ok := c.pathUint64(":id") + if !ok { + c.jsonError(400, "无效ID") + return + } + + childCount, err := models.Orm.QueryTable(new(models.BackendErpOrganization)). + Filter("parent_id", id). + Filter("delete_time__isnull", true). + Exclude("status", 0). + Count() + if err != nil { + c.jsonError(500, "检查子组织失败: "+err.Error()) + return + } + if childCount > 0 { + c.jsonError(400, "请先删除下级组织") + return + } + + now := c.nowString() + num, err := models.Orm.QueryTable(new(models.BackendErpOrganization)). + Filter("id", id). + Filter("delete_time__isnull", true). + Update(orm.Params{"delete_time": now, "status": int8(0)}) + if err != nil { + c.jsonError(500, "删除组织机构失败: "+err.Error()) + return + } + if num == 0 { + c.jsonError(404, "组织机构不存在") + return + } + + c.jsonOK(nil) +} + +// GetCompanys 获取企业单位列表。 +// GET /admin/erp/getCompanys +func (c *BackendErpController) GetCompanys() { + tid, _ := c.GetInt("tid") + + qs := models.Orm.QueryTable(new(models.BackendErpOrganization)). + Filter("delete_time__isnull", true). + Exclude("status", 0). + Filter("is_company", 1) + if tid > 0 { + qs = qs.Filter("tid", tid) + } + + var rows []models.BackendErpOrganization + _, err := qs.OrderBy("sort", "id").All(&rows) + if err != nil { + c.jsonError(500, "查询企业单位失败: "+err.Error()) + return + } + + list := make([]erpOrganizationDTO, 0, len(rows)) + for _, row := range rows { + list = append(list, c.organizationDTO(row)) + } + + c.jsonOK(list) +} + +// GetDepartments 获取部门列表。 +// GET /admin/erp/getDepartments?parent_id=1 +func (c *BackendErpController) GetDepartments() { + parentID, _ := c.GetInt("parent_id") + tid, _ := c.GetInt("tid") + + qs := models.Orm.QueryTable(new(models.BackendErpOrganization)). + Filter("delete_time__isnull", true). + Exclude("status", 0) + if parentID > 0 { + qs = qs.Filter("parent_id", parentID) + } else { + qs = qs.Filter("is_company", 0) + } + if tid > 0 { + qs = qs.Filter("tid", tid) + } + + var rows []models.BackendErpOrganization + _, err := qs.OrderBy("sort", "id").All(&rows) + if err != nil { + c.jsonError(500, "查询部门失败: "+err.Error()) + return + } + + list := make([]erpOrganizationDTO, 0, len(rows)) + for _, row := range rows { + list = append(list, c.organizationDTO(row)) + } + + c.jsonOK(list) +} + +// GetEmployee 获取员工列表。 +// GET /admin/erp/getEmployee?tid=1 +func (c *BackendErpController) GetEmployee() { + tid, _ := c.GetInt("tid") + + qs := models.Orm.QueryTable(new(models.BackendErpEmployee)).Filter("delete_time__isnull", true) + if tid > 0 { + qs = qs.Filter("tid", tid) + } + + var rows []models.BackendErpEmployee + _, err := qs.OrderBy("-id").All(&rows) + if err != nil { + c.jsonError(500, "查询员工失败: "+err.Error()) + return + } + + list := make([]erpEmployeeDTO, 0, len(rows)) + for _, row := range rows { + list = append(list, c.employeeDTO(row)) + } + + c.jsonOK(list) +} + +// GetEmployeeDetail 获取员工详情。 +// GET /admin/erp/getEmployeeDetail/:id +func (c *BackendErpController) GetEmployeeDetail() { + id, ok := c.pathUint(":id") + if !ok { + c.jsonError(400, "无效ID") + return + } + + var row models.BackendErpEmployee + err := models.Orm.QueryTable(new(models.BackendErpEmployee)). + Filter("id", id). + Filter("delete_time__isnull", true). + One(&row) + if err != nil { + c.jsonError(404, "员工不存在") + return + } + + c.jsonOK(c.employeeDTO(row)) +} + +// CreateEmployee 创建员工。 +// POST /admin/erp/createEmployee +func (c *BackendErpController) CreateEmployee() { + body := c.parseJSONBody() + + name, _ := c.getStringValue(body, "name") + name = strings.TrimSpace(name) + if name == "" { + c.jsonError(400, "姓名不能为空") + return + } + + account, _ := c.getStringValue(body, "account") + account = strings.TrimSpace(account) + if account == "" { + account = "EMP" + c.nowCompactString() + } + + tid, _ := c.getIntValue(body, "tid", "tenant_id") + gender, hasGender := c.getIntValue(body, "gender", "sex") + status, hasStatus := c.getIntValue(body, "account_status", "status") + password, _ := c.getStringValue(body, "password") + birthday, _ := c.getStringValue(body, "birthday") + affiliateUnit, _ := c.getStringValue(body, "affiliate_unit") + department, _ := c.getStringValue(body, "department") + position, _ := c.getStringValue(body, "position") + education, _ := c.getStringValue(body, "education") + nation, _ := c.getStringValue(body, "nation") + phone, _ := c.getStringValue(body, "phone") + wechat, _ := c.getStringValue(body, "wechat") + email, _ := c.getStringValue(body, "email") + homeAddress, _ := c.getStringValue(body, "home_address") + + row := models.BackendErpEmployee{ + Tid: nullableIntPtr(tid), + Account: account, + Password: hashEmployeePassword(password), + Name: name, + Gender: 0, + Birthday: parseDatePtr(birthday), + AffiliateUnit: strPtrIfNotEmpty(affiliateUnit), + Department: strPtrIfNotEmpty(department), + Position: strPtrIfNotEmpty(position), + Education: strPtrIfNotEmpty(education), + Nation: strPtrIfNotEmpty(nation), + Phone: strPtrIfNotEmpty(phone), + Wechat: strPtrIfNotEmpty(wechat), + Email: strPtrIfNotEmpty(email), + HomeAddress: strPtrIfNotEmpty(homeAddress), + AccountStatus: 1, + } + if hasGender { + row.Gender = int8(gender) + } + if hasStatus { + row.AccountStatus = int8(status) + } + + id, err := models.Orm.Insert(&row) + if err != nil { + c.jsonError(500, "创建员工失败: "+err.Error()) + return + } + + c.jsonOK(map[string]interface{}{"id": id}) +} + +// EditEmployee 更新员工。 +// POST /admin/erp/editEmployee/:id +func (c *BackendErpController) EditEmployee() { + id, ok := c.pathUint(":id") + if !ok { + c.jsonError(400, "无效ID") + return + } + + body := c.parseJSONBody() + update := orm.Params{} + + if v, has := c.getStringValue(body, "account"); has && strings.TrimSpace(v) != "" { + update["account"] = strings.TrimSpace(v) + } + if v, has := c.getStringValue(body, "name"); has { + v = strings.TrimSpace(v) + if v == "" { + c.jsonError(400, "姓名不能为空") + return + } + update["name"] = v + } + if v, has := c.getIntValue(body, "tid", "tenant_id"); has { + update["tid"] = nullableInt(v) + } + if v, has := c.getIntValue(body, "gender", "sex"); has { + update["gender"] = int8(v) + } + if v, has := c.getStringValue(body, "birthday"); has { + update["birthday"] = parseDatePtr(v) + } + if v, has := c.getStringValue(body, "affiliate_unit"); has { + update["affiliate_unit"] = nullableString(v) + } + if v, has := c.getStringValue(body, "department"); has { + update["department"] = nullableString(v) + } + if v, has := c.getStringValue(body, "position"); has { + update["position"] = nullableString(v) + } + if v, has := c.getStringValue(body, "education"); has { + update["education"] = nullableString(v) + } + if v, has := c.getStringValue(body, "nation"); has { + update["nation"] = nullableString(v) + } + if v, has := c.getStringValue(body, "phone"); has { + update["phone"] = nullableString(v) + } + if v, has := c.getStringValue(body, "wechat"); has { + update["wechat"] = nullableString(v) + } + if v, has := c.getStringValue(body, "email"); has { + update["email"] = nullableString(v) + } + if v, has := c.getStringValue(body, "home_address"); has { + update["home_address"] = nullableString(v) + } + if v, has := c.getIntValue(body, "account_status", "status"); has { + update["account_status"] = int8(v) + } + if v, has := c.getStringValue(body, "password"); has && strings.TrimSpace(v) != "" { + update["password"] = hashEmployeePassword(v) + } + + if len(update) == 0 { + c.jsonError(400, "无更新字段") + return + } + + num, err := models.Orm.QueryTable(new(models.BackendErpEmployee)). + Filter("id", id). + Filter("delete_time__isnull", true). + Update(update) + if err != nil { + c.jsonError(500, "更新员工失败: "+err.Error()) + return + } + if num == 0 { + c.jsonError(404, "员工不存在") + return + } + + c.jsonOK(nil) +} + +// DeleteEmployee 删除员工。 +// DELETE /admin/erp/deleteEmployee/:id +func (c *BackendErpController) DeleteEmployee() { + id, ok := c.pathUint(":id") + if !ok { + c.jsonError(400, "无效ID") + return + } + + num, err := models.Orm.QueryTable(new(models.BackendErpEmployee)). + Filter("id", id). + Filter("delete_time__isnull", true). + Update(orm.Params{"delete_time": c.nowString(), "account_status": int8(2)}) + if err != nil { + c.jsonError(500, "删除员工失败: "+err.Error()) + return + } + if num == 0 { + c.jsonError(404, "员工不存在") + return + } + + c.jsonOK(nil) +} + +// GetPosition 获取职位列表。 +// GET /admin/erp/getPosition +func (c *BackendErpController) GetPosition() { + tid, _ := c.GetInt("tid") + departmentID, _ := c.GetInt("department_id") + + qs := models.Orm.QueryTable(new(models.BackendErpPosition)) + if tid > 0 { + qs = qs.Filter("tenant_id", tid) + } + if departmentID > 0 { + qs = qs.Filter("department_id", departmentID) + } + + var rows []models.BackendErpPosition + _, err := qs.OrderBy("sort", "id").All(&rows) + if err != nil { + c.jsonError(500, "查询职位失败: "+err.Error()) + return + } + + list := make([]erpPositionDTO, 0, len(rows)) + for _, row := range rows { + list = append(list, c.positionDTO(row)) + } + + c.jsonOK(list) +} + +// GetPositionDetail 获取职位详情。 +// GET /admin/erp/getPositionDetail/:id +func (c *BackendErpController) GetPositionDetail() { + id, ok := c.pathUint64(":id") + if !ok { + c.jsonError(400, "无效ID") + return + } + + var row models.BackendErpPosition + err := models.Orm.QueryTable(new(models.BackendErpPosition)).Filter("id", id).One(&row) + if err != nil { + c.jsonError(404, "职位不存在") + return + } + + c.jsonOK(c.positionDTO(row)) +} + +// CreatePosition 创建职位。 +// POST /admin/erp/createPosition +func (c *BackendErpController) CreatePosition() { + body := c.parseJSONBody() + + tenantID, _ := c.getUint64Value(body, "tenant_id", "tid") + departmentID, _ := c.getUint64Value(body, "department_id") + positionName, _ := c.getStringValue(body, "position_name", "name") + positionName = strings.TrimSpace(positionName) + if positionName == "" { + c.jsonError(400, "职位名称不能为空") + return + } + + positionCode, _ := c.getStringValue(body, "position_code", "code") + positionCode = strings.TrimSpace(positionCode) + if positionCode == "" { + positionCode = "POS" + c.nowCompactString() + } + positionType, _ := c.getIntValue(body, "position_type") + status, hasStatus := c.getIntValue(body, "status") + sortVal, _ := c.getUintValue(body, "sort") + + row := models.BackendErpPosition{ + TenantID: tenantID, + DepartmentID: departmentID, + PositionCode: positionCode, + PositionName: positionName, + PositionType: int8(positionType), + Status: 1, + Sort: sortVal, + } + if hasStatus { + row.Status = int8(status) + } + + id, err := models.Orm.Insert(&row) + if err != nil { + c.jsonError(500, "创建职位失败: "+err.Error()) + return + } + + c.jsonOK(map[string]interface{}{"id": id}) +} + +// EditPosition 更新职位。 +// POST /admin/erp/editPosition/:id +func (c *BackendErpController) EditPosition() { + id, ok := c.pathUint64(":id") + if !ok { + c.jsonError(400, "无效ID") + return + } + + body := c.parseJSONBody() + update := orm.Params{} + + if v, has := c.getUint64Value(body, "tenant_id", "tid"); has { + update["tenant_id"] = v + } + if v, has := c.getUint64Value(body, "department_id"); has { + update["department_id"] = v + } + if v, has := c.getStringValue(body, "position_code", "code"); has { + update["position_code"] = strings.TrimSpace(v) + } + if v, has := c.getStringValue(body, "position_name", "name"); has { + v = strings.TrimSpace(v) + if v == "" { + c.jsonError(400, "职位名称不能为空") + return + } + update["position_name"] = v + } + if v, has := c.getIntValue(body, "position_type"); has { + update["position_type"] = int8(v) + } + if v, has := c.getIntValue(body, "status"); has { + update["status"] = int8(v) + } + if v, has := c.getUintValue(body, "sort"); has { + update["sort"] = v + } + + if len(update) == 0 { + c.jsonError(400, "无更新字段") + return + } + + num, err := models.Orm.QueryTable(new(models.BackendErpPosition)).Filter("id", id).Update(update) + if err != nil { + c.jsonError(500, "更新职位失败: "+err.Error()) + return + } + if num == 0 { + c.jsonError(404, "职位不存在") + return + } + + c.jsonOK(nil) +} + +// DeletePosition 删除职位。 +// DELETE /admin/erp/deletePosition/:id +func (c *BackendErpController) DeletePosition() { + id, ok := c.pathUint64(":id") + if !ok { + c.jsonError(400, "无效ID") + return + } + + num, err := models.Orm.QueryTable(new(models.BackendErpPosition)).Filter("id", id).Delete() + if err != nil { + c.jsonError(500, "删除职位失败: "+err.Error()) + return + } + if num == 0 { + c.jsonError(404, "职位不存在") + return + } + + c.jsonOK(nil) +} + +func (c *BackendErpController) organizationDTO(row models.BackendErpOrganization) erpOrganizationDTO { + parentName := "" + if row.ParentID > 0 { + var parent models.BackendErpOrganization + if err := models.Orm.QueryTable(new(models.BackendErpOrganization)). + Filter("id", row.ParentID). + One(&parent); err == nil { + parentName = parent.OrgName + } + } + + leaderID := uint64(0) + leaderName := "" + if row.LeaderID != nil { + leaderID = *row.LeaderID + var employee models.BackendErpEmployee + if err := models.Orm.QueryTable(new(models.BackendErpEmployee)). + Filter("id", leaderID). + One(&employee); err == nil { + leaderName = employee.Name + } + } + + return erpOrganizationDTO{ + ID: row.ID, + Tid: row.Tid, + TenantID: row.Tid, + OrgName: row.OrgName, + OrgCode: row.OrgCode, + ParentID: row.ParentID, + ParentName: parentName, + LeaderID: leaderID, + LeaderName: leaderName, + IsCompany: row.IsCompany, + Sort: row.Sort, + Status: row.Status, + Remark: derefString(row.Remark), + } +} + +func (c *BackendErpController) employeeDTO(row models.BackendErpEmployee) erpEmployeeDTO { + tid := 0 + if row.Tid != nil { + tid = *row.Tid + } + + birthday := "" + if row.Birthday != nil { + birthday = row.Birthday.Format("2006-01-02") + } + + affiliateUnit := derefString(row.AffiliateUnit) + department := derefString(row.Department) + affiliateUnitName := c.organizationNameByIDString(affiliateUnit) + departmentName := c.organizationNameByIDString(department) + + return erpEmployeeDTO{ + ID: row.ID, + Tid: tid, + TenantID: tid, + Account: row.Account, + Name: row.Name, + Gender: row.Gender, + Sex: row.Gender, + Birthday: birthday, + AffiliateUnit: affiliateUnit, + AffiliateUnitName: affiliateUnitName, + Department: department, + DepartmentName: departmentName, + Position: derefString(row.Position), + Education: derefString(row.Education), + Nation: derefString(row.Nation), + Phone: derefString(row.Phone), + Wechat: derefString(row.Wechat), + Email: derefString(row.Email), + HomeAddress: derefString(row.HomeAddress), + AccountStatus: row.AccountStatus, + Status: row.AccountStatus, + } +} + +func (c *BackendErpController) organizationNameByIDString(idValue string) string { + idValue = strings.TrimSpace(idValue) + if idValue == "" { + return "" + } + + id, err := strconv.ParseUint(idValue, 10, 64) + if err != nil || id == 0 { + return "" + } + + var org models.BackendErpOrganization + err = models.Orm.QueryTable(new(models.BackendErpOrganization)). + Filter("id", id). + Filter("delete_time__isnull", true). + One(&org) + if err != nil { + return "" + } + + return org.OrgName +} + +func (c *BackendErpController) positionDTO(row models.BackendErpPosition) erpPositionDTO { + return erpPositionDTO{ + ID: row.ID, + TenantID: row.TenantID, + Tid: row.TenantID, + DepartmentID: row.DepartmentID, + PositionCode: row.PositionCode, + PositionName: row.PositionName, + PositionType: row.PositionType, + Status: row.Status, + Sort: row.Sort, + } +} + +func (c *BackendErpController) parseJSONBody() map[string]interface{} { + body := map[string]interface{}{} + contentType := strings.ToLower(c.Ctx.Input.Header("Content-Type")) + if !strings.Contains(contentType, "json") { + return body + } + if len(c.Ctx.Input.RequestBody) == 0 { + return body + } + _ = json.Unmarshal(c.Ctx.Input.RequestBody, &body) + return body +} + +func (c *BackendErpController) getStringValue(body map[string]interface{}, keys ...string) (string, bool) { + for _, key := range keys { + if v, ok := body[key]; ok { + switch val := v.(type) { + case string: + return val, true + case float64: + return strconv.FormatFloat(val, 'f', -1, 64), true + case bool: + return strconv.FormatBool(val), true + default: + return strings.TrimSpace(strings.Trim(strings.ReplaceAll(strings.ReplaceAll(toJSON(val), "\n", ""), "\r", ""), "\"")), true + } + } + if c.Ctx.Request.Form == nil && c.Ctx.Request.PostForm == nil && c.Ctx.Request.MultipartForm == nil { + _ = c.Ctx.Request.ParseMultipartForm(32 << 20) + } + if val := c.GetString(key); val != "" { + return val, true + } + } + return "", false +} + +func (c *BackendErpController) getIntValue(body map[string]interface{}, keys ...string) (int, bool) { + for _, key := range keys { + if v, ok := body[key]; ok { + switch val := v.(type) { + case float64: + return int(val), true + case int: + return val, true + case string: + if strings.TrimSpace(val) == "" { + return 0, true + } + parsed, err := strconv.Atoi(strings.TrimSpace(val)) + return parsed, err == nil + } + } + if c.Ctx.Request.Form == nil && c.Ctx.Request.PostForm == nil && c.Ctx.Request.MultipartForm == nil { + _ = c.Ctx.Request.ParseMultipartForm(32 << 20) + } + if val := c.GetString(key); val != "" { + parsed, err := strconv.Atoi(strings.TrimSpace(val)) + return parsed, err == nil + } + } + return 0, false +} + +func (c *BackendErpController) getUintValue(body map[string]interface{}, keys ...string) (uint, bool) { + v, ok := c.getIntValue(body, keys...) + if !ok || v < 0 { + return 0, ok + } + return uint(v), true +} + +func (c *BackendErpController) getUint64Value(body map[string]interface{}, keys ...string) (uint64, bool) { + for _, key := range keys { + if v, ok := body[key]; ok { + switch val := v.(type) { + case float64: + if val < 0 { + return 0, false + } + return uint64(val), true + case int: + if val < 0 { + return 0, false + } + return uint64(val), true + case string: + if strings.TrimSpace(val) == "" { + return 0, true + } + parsed, err := strconv.ParseUint(strings.TrimSpace(val), 10, 64) + return parsed, err == nil + } + } + if c.Ctx.Request.Form == nil && c.Ctx.Request.PostForm == nil && c.Ctx.Request.MultipartForm == nil { + _ = c.Ctx.Request.ParseMultipartForm(32 << 20) + } + if val := c.GetString(key); val != "" { + parsed, err := strconv.ParseUint(strings.TrimSpace(val), 10, 64) + return parsed, err == nil + } + } + return 0, false +} + +func (c *BackendErpController) pathUint(name string) (uint, bool) { + id, err := strconv.ParseUint(c.Ctx.Input.Param(name), 10, 64) + return uint(id), err == nil && id > 0 +} + +func (c *BackendErpController) pathUint64(name string) (uint64, bool) { + id, err := strconv.ParseUint(c.Ctx.Input.Param(name), 10, 64) + return id, err == nil && id > 0 +} + +func (c *BackendErpController) jsonOK(data interface{}) { + resp := map[string]interface{}{"code": 200, "msg": "success"} + if data != nil { + resp["data"] = data + } + c.Data["json"] = resp + _ = c.ServeJSON() +} + +func (c *BackendErpController) jsonError(code int, msg string) { + c.Data["json"] = map[string]interface{}{"code": code, "msg": msg} + _ = c.ServeJSON() +} + +func (c *BackendErpController) nowString() string { + return time.Now().Format("2006-01-02 15:04:05") +} + +func (c *BackendErpController) nowCompactString() string { + return strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(c.nowString(), "-", ""), ":", ""), " ", "") +} + +func strPtrIfNotEmpty(v string) *string { + v = strings.TrimSpace(v) + if v == "" { + return nil + } + return &v +} + +func nullableString(v string) interface{} { + v = strings.TrimSpace(v) + if v == "" { + return nil + } + return v +} + +func nullableInt(v int) interface{} { + if v <= 0 { + return nil + } + return v +} + +func nullableIntPtr(v int) *int { + if v <= 0 { + return nil + } + return &v +} + +func nullableUint64(v uint64) interface{} { + if v == 0 { + return nil + } + return v +} + +func derefString(v *string) string { + if v == nil { + return "" + } + return *v +} + +func boolInt(v bool) int { + if v { + return 1 + } + return 0 +} + +func toJSON(v interface{}) string { + b, _ := json.Marshal(v) + return string(b) +} + +func parseDatePtr(v string) *time.Time { + v = strings.TrimSpace(v) + if v == "" { + return nil + } + if t, err := time.Parse("2006-01-02", v); err == nil { + return &t + } + if t, err := time.Parse("2006-01-02 15:04:05", v); err == nil { + return &t + } + return nil +} + +// hashEmployeePassword 适配 yz_backend_erp_employee.password varchar(64),使用 sha256 hex。 +// 如果密码为空则返回空字符串,符合表默认值。 +func hashEmployeePassword(plain string) string { + plain = strings.TrimSpace(plain) + if plain == "" { + return "" + } + sum := sha256.Sum256([]byte(plain)) + return hex.EncodeToString(sum[:]) +} diff --git a/models/erp.go b/models/erp.go new file mode 100644 index 0000000..79153e9 --- /dev/null +++ b/models/erp.go @@ -0,0 +1,73 @@ +package models + +import "time" + +// BackendErpOrganization 组织架构表 yz_backend_erp_organization +type BackendErpOrganization struct { + ID uint64 `orm:"column(id);pk;auto" json:"id"` + Tid uint64 `orm:"column(tid)" json:"tid"` + OrgName string `orm:"column(org_name);size(128)" json:"org_name"` + OrgCode string `orm:"column(org_code);size(64)" json:"org_code"` + ParentID uint64 `orm:"column(parent_id);default(0)" json:"parent_id"` + Sort uint `orm:"column(sort);default(0)" json:"sort"` + LeaderID *uint64 `orm:"column(leader_id);null" json:"leader_id"` + IsCompany int `orm:"column(is_company);default(0)" json:"is_company"` + Status int8 `orm:"column(status);default(1)" json:"status"` + 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"` + DeleteTime *time.Time `orm:"column(delete_time);type(datetime);null" json:"delete_time"` + Remark *string `orm:"column(remark);size(512);null" json:"remark"` +} + +// TableName 自定义表名 +func (m *BackendErpOrganization) TableName() string { + return "yz_backend_erp_organization" +} + +// BackendErpEmployee 员工信息表 yz_backend_erp_employee +type BackendErpEmployee struct { + ID uint `orm:"column(id);pk;auto" json:"id"` + Tid *int `orm:"column(tid);null" json:"tid"` + Account string `orm:"column(account);size(50)" json:"account"` + Password string `orm:"column(password);size(64);default()" json:"-"` + Name string `orm:"column(name);size(30)" json:"name"` + Gender int8 `orm:"column(gender);default(0)" json:"gender"` + Birthday *time.Time `orm:"column(birthday);type(date);null" json:"birthday"` + AffiliateUnit *string `orm:"column(affiliate_unit);size(100);null" json:"affiliate_unit"` + Department *string `orm:"column(department);size(50);null" json:"department"` + Position *string `orm:"column(position);size(50);null" json:"position"` + Education *string `orm:"column(education);size(20);null" json:"education"` + Nation *string `orm:"column(nation);size(20);null" json:"nation"` + Phone *string `orm:"column(phone);size(20);null" json:"phone"` + Wechat *string `orm:"column(wechat);size(50);null" json:"wechat"` + Email *string `orm:"column(email);size(100);null" json:"email"` + HomeAddress *string `orm:"column(home_address);size(255);null" json:"home_address"` + AccountStatus int8 `orm:"column(account_status);default(1)" json:"account_status"` + 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"` + DeleteTime *time.Time `orm:"column(delete_time);type(datetime);null" json:"delete_time"` +} + +// TableName 自定义表名 +func (m *BackendErpEmployee) TableName() string { + return "yz_backend_erp_employee" +} + +// BackendErpPosition 职位表 yz_backend_erp_position +type BackendErpPosition struct { + ID uint64 `orm:"column(id);pk;auto" json:"id"` + TenantID uint64 `orm:"column(tenant_id)" json:"tenant_id"` + DepartmentID uint64 `orm:"column(department_id)" json:"department_id"` + PositionCode string `orm:"column(position_code);size(50)" json:"position_code"` + PositionName string `orm:"column(position_name);size(100)" json:"position_name"` + PositionType int8 `orm:"column(position_type);default(0)" json:"position_type"` + Status int8 `orm:"column(status);default(1)" json:"status"` + Sort uint `orm:"column(sort);default(0)" json:"sort"` + 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"` +} + +// TableName 自定义表名 +func (m *BackendErpPosition) TableName() string { + return "yz_backend_erp_position" +} diff --git a/models/init.go b/models/init.go index fe99dfc..0f78f16 100644 --- a/models/init.go +++ b/models/init.go @@ -35,6 +35,9 @@ func Init(_ string) { orm.RegisterModel( new(Tenant), new(TenantUser), + new(BackendErpOrganization), + new(BackendErpEmployee), + new(BackendErpPosition), new(SystemMenu), new(AdminUser), new(AdminRole), diff --git a/routers/backend/backend.go b/routers/backend/backend.go index 98ff46a..74fa60c 100644 --- a/routers/backend/backend.go +++ b/routers/backend/backend.go @@ -51,4 +51,32 @@ func RegisterAuthRoutes() { // 租户站点设置(yz_tenant_site_setting) beego.Router("/backend/normalInfos", &controllers.SiteSettingsController{}, "get:GetNormalInfos") beego.Router("/backend/saveNormalInfos", &controllers.SiteSettingsController{}, "post:SaveNormalInfos") + + // 兼容旧 backend 前端 /admin/* 用户接口 + beego.Router("/admin/getTenantUsers/:tid", &controllers.PlatformTenantUserController{}, "get:GetTenantUsersByTid") + beego.Router("/admin/getAllUsers", &controllers.PlatformAdminUserController{}, "get:GetAllUsers") + beego.Router("/admin/getUserInfo/:id", &controllers.PlatformAdminUserController{}, "get:GetUserInfo") + beego.Router("/admin/addUser", &controllers.PlatformAdminUserController{}, "post:AddUser") + beego.Router("/admin/editUser/:id", &controllers.PlatformAdminUserController{}, "post:EditUser") + beego.Router("/admin/deleteUser/:id", &controllers.PlatformAdminUserController{}, "delete:DeleteUser") + beego.Router("/admin/changePassword", &controllers.PlatformAdminUserController{}, "post:ChangePassword") + + // 兼容旧 backend 前端 /admin/erp/* ERP 接口 + beego.Router("/admin/erp/getOrganization", &controllers.BackendErpController{}, "get:GetOrganization") + beego.Router("/admin/erp/getOrganizationDetail/:id", &controllers.BackendErpController{}, "get:GetOrganizationDetail") + beego.Router("/admin/erp/createOrganization", &controllers.BackendErpController{}, "post:CreateOrganization") + beego.Router("/admin/erp/editOrganization/:id", &controllers.BackendErpController{}, "post:EditOrganization") + beego.Router("/admin/erp/deleteOrganization/:id", &controllers.BackendErpController{}, "delete:DeleteOrganization") + beego.Router("/admin/erp/getCompanys", &controllers.BackendErpController{}, "get:GetCompanys") + beego.Router("/admin/erp/getDepartments", &controllers.BackendErpController{}, "get:GetDepartments") + beego.Router("/admin/erp/getEmployee", &controllers.BackendErpController{}, "get:GetEmployee") + beego.Router("/admin/erp/getEmployeeDetail/:id", &controllers.BackendErpController{}, "get:GetEmployeeDetail") + beego.Router("/admin/erp/createEmployee", &controllers.BackendErpController{}, "post:CreateEmployee") + beego.Router("/admin/erp/editEmployee/:id", &controllers.BackendErpController{}, "post:EditEmployee") + beego.Router("/admin/erp/deleteEmployee/:id", &controllers.BackendErpController{}, "delete:DeleteEmployee") + beego.Router("/admin/erp/getPosition", &controllers.BackendErpController{}, "get:GetPosition") + beego.Router("/admin/erp/getPositionDetail/:id", &controllers.BackendErpController{}, "get:GetPositionDetail") + beego.Router("/admin/erp/createPosition", &controllers.BackendErpController{}, "post:CreatePosition") + beego.Router("/admin/erp/editPosition/:id", &controllers.BackendErpController{}, "post:EditPosition") + beego.Router("/admin/erp/deletePosition/:id", &controllers.BackendErpController{}, "delete:DeletePosition") }