Files
yunzerwebsiteallinone/go/controllers/backend_employee_file.go
T
2026-08-28 18:01:09 +08:00

847 lines
25 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package controllers
import (
"encoding/json"
"io"
"regexp"
"strconv"
"strings"
"time"
"server/models"
"server/pkg/jwtutil"
"github.com/beego/beego/v2/client/orm"
beego "github.com/beego/beego/v2/server/web"
)
// BackendEmployeeFileController 员工档案管理(OA 人事档案)。
// 档案与员工(yz_backend_employee)按 tid + employee_id 一对一,
// 子记录表统一存放教育经历 / 工作经历 / 合同信息 / 证照附件。
// 数据按 JWT 中的租户隔离。
type BackendEmployeeFileController struct {
beego.Controller
}
func (c *BackendEmployeeFileController) efClaims() (*jwtutil.Claims, error) {
auth := c.Ctx.Request.Header.Get("Authorization")
if auth == "" {
return nil, orm.ErrNoRows
}
parts := strings.SplitN(auth, " ", 2)
if len(parts) != 2 || parts[0] != "Bearer" {
return nil, orm.ErrNoRows
}
claims, err := jwtutil.ParseToken(parts[1])
if err != nil {
return nil, err
}
if claims.UserType != "backend" {
return nil, orm.ErrNoRows
}
return claims, nil
}
func (c *BackendEmployeeFileController) efOk(data interface{}) {
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": data}
_ = c.ServeJSON()
}
func (c *BackendEmployeeFileController) efErr(httpStatus, bizCode int, msg string) {
c.Ctx.Output.SetStatus(httpStatus)
c.Data["json"] = map[string]interface{}{"code": bizCode, "msg": msg}
_ = c.ServeJSON()
}
var efDatePattern = regexp.MustCompile(`^\d{4}-\d{2}-\d{2}$`)
// efIsValidDate 校验 "YYYY-MM-DD" 日期字符串;允许为空(可空字段)。
func efIsValidDate(s string) bool {
s = strings.TrimSpace(s)
if s == "" {
return true
}
if !efDatePattern.MatchString(s) {
return false
}
_, err := time.ParseInLocation("2006-01-02", s, time.Local)
return err == nil
}
// efNormalizeDate 将 ORM 读出的日期值统一为 "YYYY-MM-DD"。
// DSN 开启 parseTime=True 后 DATE 列会被驱动解析为 time.Time,
// 赋给 string 字段后是 Go 默认格式,取前 10 位(与 OA 日程一致)。
func efNormalizeDate(s string) string {
s = strings.TrimSpace(s)
if len(s) >= 10 {
return s[:10]
}
return s
}
// efQuery 档案查询基座:强制附加租户过滤与未删除条件。
func (c *BackendEmployeeFileController) efQuery(tid int) orm.QuerySeter {
return models.Orm.QueryTable(new(models.BackendEmployeeFile)).
Filter("tid", tid).
Filter("is_deleted", 0)
}
// efEmployeeQuery 员工查询基座(与组织架构控制器保持同一过滤口径)。
func efEmployeeQuery(tid int) orm.QuerySeter {
return models.Orm.QueryTable(new(models.BackendEmployee)).
Filter("tid", tid).
Filter("delete_time__isnull", true)
}
// efOrgNameMap 组织ID -> 名称映射,用于展示员工的隶属单位/部门名称。
func (c *BackendEmployeeFileController) efOrgNameMap(tid int) map[uint64]string {
var rows []models.BackendOrganization
if _, err := models.Orm.QueryTable(new(models.BackendOrganization)).
Filter("tid", tid).
Filter("delete_time__isnull", true).
All(&rows, "ID", "OrgName"); err != nil {
return map[uint64]string{}
}
names := make(map[uint64]string, len(rows))
for _, row := range rows {
names[row.ID] = row.OrgName
}
return names
}
// efOrgNameByID 组织ID字符串转名称;非数字时原样返回(手输文本兜底)。
func efOrgNameByID(names map[uint64]string, id string) string {
if id == "" {
return ""
}
if v, err := strconv.ParseUint(id, 10, 64); err == nil {
return names[v]
}
return id
}
// efSubtreeOrgIDs 收集组织节点及其全部下级的ID(BFS,含环保护)。
func (c *BackendEmployeeFileController) efSubtreeOrgIDs(tid int, rootID uint64) []uint64 {
var rows []models.BackendOrganization
if _, err := models.Orm.QueryTable(new(models.BackendOrganization)).
Filter("tid", tid).
Filter("delete_time__isnull", true).
All(&rows, "ID", "ParentID"); err != nil {
return []uint64{rootID}
}
childrenOf := map[uint64][]uint64{}
for _, row := range rows {
childrenOf[row.ParentID] = append(childrenOf[row.ParentID], row.ID)
}
result := []uint64{rootID}
queue := []uint64{rootID}
visited := map[uint64]bool{rootID: true}
for len(queue) > 0 {
current := queue[0]
queue = queue[1:]
for _, child := range childrenOf[current] {
if visited[child] {
continue
}
visited[child] = true
result = append(result, child)
queue = append(queue, child)
}
}
return result
}
// efEmployeeIDsInOrgs 取部门集合内的员工ID(档案按员工过滤时使用)。
func (c *BackendEmployeeFileController) efEmployeeIDsInOrgs(tid int, orgIDs []uint64) []uint64 {
values := make([]string, 0, len(orgIDs))
for _, id := range orgIDs {
values = append(values, strconv.FormatUint(id, 10))
}
var rows []models.BackendEmployee
if _, err := efEmployeeQuery(tid).Filter("department__in", values).All(&rows, "ID"); err != nil {
return nil
}
ids := make([]uint64, 0, len(rows))
for _, row := range rows {
ids = append(ids, uint64(row.ID))
}
return ids
}
// efEmployeeIDsByKeyword 取姓名/账号/手机命中的员工ID(档案关键词搜索联动员工字段)。
func (c *BackendEmployeeFileController) efEmployeeIDsByKeyword(tid int, keyword string) []uint64 {
cond := orm.NewCondition().
Or("name__icontains", keyword).
Or("account__icontains", keyword).
Or("phone__icontains", keyword)
var rows []models.BackendEmployee
if _, err := efEmployeeQuery(tid).SetCond(cond).All(&rows, "ID"); err != nil {
return nil
}
ids := make([]uint64, 0, len(rows))
for _, row := range rows {
ids = append(ids, uint64(row.ID))
}
return ids
}
// employeeFileDTO 档案 + 关联员工展示信息(JSON 平铺,前端表格/详情直接用)。
type employeeFileDTO struct {
models.BackendEmployeeFile
EmployeeName string `json:"employee_name"`
EmployeeAccount string `json:"employee_account"`
Gender int8 `json:"gender"`
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"`
Phone string `json:"phone"`
Email string `json:"email"`
Education string `json:"education"`
Nation string `json:"nation"`
HomeAddress string `json:"home_address"`
AccountStatus int8 `json:"account_status"`
}
// efAssembleDTOList 档案列表批量组装员工展示信息。
func (c *BackendEmployeeFileController) efAssembleDTOList(tid int, files []models.BackendEmployeeFile) []employeeFileDTO {
list := make([]employeeFileDTO, 0, len(files))
if len(files) == 0 {
return list
}
empIDs := make([]uint64, 0, len(files))
for _, f := range files {
empIDs = append(empIDs, f.EmployeeID)
}
var emps []models.BackendEmployee
if _, err := efEmployeeQuery(tid).Filter("id__in", empIDs).All(&emps); err != nil {
emps = nil
}
empByID := make(map[uint64]models.BackendEmployee, len(emps))
for _, e := range emps {
empByID[uint64(e.ID)] = e
}
orgNames := c.efOrgNameMap(tid)
for _, f := range files {
dto := employeeFileDTO{BackendEmployeeFile: f}
dto.HireDate = efNormalizeDate(f.HireDate)
dto.RegularDate = efNormalizeDate(f.RegularDate)
dto.LeaveDate = efNormalizeDate(f.LeaveDate)
if emp, ok := empByID[f.EmployeeID]; ok {
birthday := ""
if emp.Birthday != nil {
birthday = emp.Birthday.Format("2006-01-02")
}
affiliate := derefString(emp.AffiliateUnit)
department := derefString(emp.Department)
dto.EmployeeName = emp.Name
dto.EmployeeAccount = emp.Account
dto.Gender = emp.Gender
dto.Birthday = birthday
dto.AffiliateUnit = affiliate
dto.AffiliateUnitName = efOrgNameByID(orgNames, affiliate)
dto.Department = department
dto.DepartmentName = efOrgNameByID(orgNames, department)
dto.Position = derefString(emp.Position)
dto.Phone = derefString(emp.Phone)
dto.Email = derefString(emp.Email)
dto.Education = derefString(emp.Education)
dto.Nation = derefString(emp.Nation)
dto.HomeAddress = derefString(emp.HomeAddress)
dto.AccountStatus = emp.AccountStatus
}
list = append(list, dto)
}
return list
}
// List GET /backend/oa/employeefile/list
// 分页查询档案,支持 keyword(档案编号/身份证/员工姓名/账号/手机)、
// status(在职状态)、org_id(部门,含下级)筛选。
func (c *BackendEmployeeFileController) List() {
claims, err := c.efClaims()
if err != nil {
c.efErr(401, 401, "未登录或无权限")
return
}
tid := claims.TenantId
page, _ := c.GetInt("page", 1)
pageSize, _ := c.GetInt("pageSize", 10)
keyword := strings.TrimSpace(c.GetString("keyword"))
statusRaw := strings.TrimSpace(c.GetString("status"))
orgID, _ := c.GetUint64("org_id")
if page < 1 {
page = 1
}
if pageSize < 1 || pageSize > 200 {
pageSize = 10
}
qs := c.efQuery(tid)
if orgID > 0 {
empIDs := c.efEmployeeIDsInOrgs(tid, c.efSubtreeOrgIDs(tid, orgID))
if len(empIDs) == 0 {
c.efOk(map[string]interface{}{"list": []employeeFileDTO{}, "total": 0})
return
}
qs = qs.Filter("employee_id__in", empIDs)
}
if statusRaw != "" {
status, convErr := strconv.Atoi(statusRaw)
if convErr != nil || status < 1 || status > 3 {
c.efErr(400, 400, "在职状态参数无效")
return
}
qs = qs.Filter("employment_status", int8(status))
}
if keyword != "" {
// 关键词同时命中档案自身字段(编号/身份证)与员工字段(姓名/账号/手机)
empIDs := c.efEmployeeIDsByKeyword(tid, keyword)
cond := orm.NewCondition().
Or("file_no__icontains", keyword).
Or("id_card__icontains", keyword)
if len(empIDs) > 0 {
cond = cond.Or("employee_id__in", empIDs)
}
qs = qs.SetCond(orm.NewCondition().AndCond(cond))
}
total, _ := qs.Count()
var files []models.BackendEmployeeFile
if _, err := qs.OrderBy("-id").Limit(pageSize).Offset((page - 1) * pageSize).All(&files); err != nil && err != orm.ErrNoRows {
c.efErr(500, 500, "查询失败")
return
}
c.efOk(map[string]interface{}{
"list": c.efAssembleDTOList(tid, files),
"total": total,
})
}
// Stats GET /backend/oa/employeefile/stats
// 返回档案总数与在职状态分布,供页面顶部统计卡片展示。
func (c *BackendEmployeeFileController) Stats() {
claims, err := c.efClaims()
if err != nil {
c.efErr(401, 401, "未登录或无权限")
return
}
tid := claims.TenantId
total, _ := c.efQuery(tid).Count()
probation, _ := c.efQuery(tid).Filter("employment_status", 1).Count()
active, _ := c.efQuery(tid).Filter("employment_status", 2).Count()
resigned, _ := c.efQuery(tid).Filter("employment_status", 3).Count()
c.efOk(map[string]interface{}{
"total": total,
"probation": probation,
"active": active,
"resigned": resigned,
})
}
// Detail GET /backend/oa/employeefile/detail/:id
// 返回档案详情(含员工展示信息)与全部子记录(教育/工作/合同/证照)。
func (c *BackendEmployeeFileController) Detail() {
claims, err := c.efClaims()
if err != nil {
c.efErr(401, 401, "未登录或无权限")
return
}
tid := claims.TenantId
id, err := strconv.ParseUint(c.Ctx.Input.Param(":id"), 10, 64)
if err != nil || id == 0 {
c.efErr(400, 400, "无效ID")
return
}
var file models.BackendEmployeeFile
if err := c.efQuery(tid).Filter("id", id).One(&file); err != nil {
c.efErr(404, 404, "档案不存在")
return
}
var records []models.BackendEmployeeFileRecord
if _, err := models.Orm.QueryTable(new(models.BackendEmployeeFileRecord)).
Filter("tid", tid).
Filter("is_deleted", 0).
Filter("employee_id", file.EmployeeID).
OrderBy("sort", "id").
All(&records); err != nil && err != orm.ErrNoRows {
c.efErr(500, 500, "查询档案记录失败")
return
}
if records == nil {
records = []models.BackendEmployeeFileRecord{}
}
for i := range records {
records[i].StartDate = efNormalizeDate(records[i].StartDate)
records[i].EndDate = efNormalizeDate(records[i].EndDate)
}
list := c.efAssembleDTOList(tid, []models.BackendEmployeeFile{file})
c.efOk(map[string]interface{}{"file": list[0], "records": records})
}
// employeeFilePayload 建档/编辑档案请求体。
type employeeFilePayload struct {
EmployeeID uint64 `json:"employee_id"`
FileNo string `json:"file_no"`
IDCard string `json:"id_card"`
IDCardFront string `json:"id_card_front"`
IDCardBack string `json:"id_card_back"`
EducationPhoto string `json:"education_photo"`
PoliticalStatus string `json:"political_status"`
MaritalStatus int8 `json:"marital_status"`
NativePlace string `json:"native_place"`
HouseholdAddress string `json:"household_address"`
CurrentAddress string `json:"current_address"`
EmergencyContact string `json:"emergency_contact"`
EmergencyPhone string `json:"emergency_phone"`
EmergencyRelation string `json:"emergency_relation"`
WorkEmail string `json:"work_email"`
HireDate string `json:"hire_date"`
RegularDate string `json:"regular_date"`
LeaveDate string `json:"leave_date"`
EmploymentStatus int8 `json:"employment_status"`
Remark string `json:"remark"`
}
// efParsePayload 读取并校验建档/编辑档案的请求体;校验失败时直接输出错误响应。
func (c *BackendEmployeeFileController) efParsePayload() (employeeFilePayload, bool) {
var payload employeeFilePayload
raw, err := io.ReadAll(c.Ctx.Request.Body)
if err != nil || json.Unmarshal(raw, &payload) != nil {
c.efErr(400, 400, "参数错误")
return payload, false
}
payload.FileNo = strings.TrimSpace(payload.FileNo)
payload.IDCard = strings.TrimSpace(payload.IDCard)
payload.WorkEmail = strings.TrimSpace(payload.WorkEmail)
if payload.IDCard != "" && len(payload.IDCard) > 30 {
c.efErr(400, 400, "身份证号过长")
return payload, false
}
// 证照附件地址长度限制(与列宽一致)
for label, url := range map[string]string{
"身份证正面": payload.IDCardFront,
"身份证反面": payload.IDCardBack,
"学历照片": payload.EducationPhoto,
} {
if len(url) > 500 {
c.efErr(400, 400, label+"地址过长")
return payload, false
}
}
if payload.WorkEmail != "" && !strings.Contains(payload.WorkEmail, "@") {
c.efErr(400, 400, "工作邮箱格式无效")
return payload, false
}
for label, value := range map[string]string{
"入职日期": payload.HireDate,
"转正日期": payload.RegularDate,
"离职日期": payload.LeaveDate,
} {
if !efIsValidDate(value) {
c.efErr(400, 400, label+"格式无效")
return payload, false
}
}
if payload.EmploymentStatus < 1 || payload.EmploymentStatus > 3 {
payload.EmploymentStatus = 2
}
if payload.MaritalStatus < 0 || payload.MaritalStatus > 4 {
payload.MaritalStatus = 0
}
return payload, true
}
// Create POST /backend/oa/employeefile/create
// 为员工建档;一人一档,重复建档会被唯一约束与前置校验拦截。
func (c *BackendEmployeeFileController) Create() {
claims, err := c.efClaims()
if err != nil {
c.efErr(401, 401, "未登录或无权限")
return
}
tid := claims.TenantId
payload, ok := c.efParsePayload()
if !ok {
return
}
if payload.EmployeeID == 0 {
c.efErr(400, 400, "请选择员工")
return
}
// 员工必须存在
var emp models.BackendEmployee
if err := efEmployeeQuery(tid).Filter("id", payload.EmployeeID).One(&emp); err != nil {
c.efErr(404, 404, "员工不存在")
return
}
// 一人一档
dup, _ := c.efQuery(tid).Filter("employee_id", payload.EmployeeID).Count()
if dup > 0 {
c.efErr(400, 400, "该员工已建档,请勿重复创建")
return
}
// 档案编号留空自动生成:EF + 时间戳
fileNo := payload.FileNo
if fileNo == "" {
fileNo = "EF" + time.Now().Format("20060102150405")
}
now := time.Now()
item := &models.BackendEmployeeFile{
Tid: tid,
EmployeeID: payload.EmployeeID,
FileNo: fileNo,
IDCard: payload.IDCard,
IDCardFront: payload.IDCardFront,
IDCardBack: payload.IDCardBack,
EducationPhoto: payload.EducationPhoto,
PoliticalStatus: payload.PoliticalStatus,
MaritalStatus: payload.MaritalStatus,
NativePlace: payload.NativePlace,
HouseholdAddress: payload.HouseholdAddress,
CurrentAddress: payload.CurrentAddress,
EmergencyContact: payload.EmergencyContact,
EmergencyPhone: payload.EmergencyPhone,
EmergencyRelation: payload.EmergencyRelation,
WorkEmail: payload.WorkEmail,
HireDate: strings.TrimSpace(payload.HireDate),
RegularDate: strings.TrimSpace(payload.RegularDate),
LeaveDate: strings.TrimSpace(payload.LeaveDate),
EmploymentStatus: payload.EmploymentStatus,
Remark: payload.Remark,
IsDeleted: 0,
CreateTime: now,
UpdateTime: &now,
}
if _, err := models.Orm.Insert(item); err != nil {
c.efErr(500, 500, "保存失败")
return
}
c.efOk(item)
}
// Update POST /backend/oa/employeefile/update/:id
// 更新档案基本资料;员工归属(employee_id)不允许修改。
func (c *BackendEmployeeFileController) Update() {
claims, err := c.efClaims()
if err != nil {
c.efErr(401, 401, "未登录或无权限")
return
}
tid := claims.TenantId
id, err := strconv.ParseUint(c.Ctx.Input.Param(":id"), 10, 64)
if err != nil || id == 0 {
c.efErr(400, 400, "无效ID")
return
}
payload, ok := c.efParsePayload()
if !ok {
return
}
var item models.BackendEmployeeFile
if err := c.efQuery(tid).Filter("id", id).One(&item); err != nil {
c.efErr(404, 404, "档案不存在")
return
}
now := time.Now()
item.FileNo = payload.FileNo
item.IDCard = payload.IDCard
item.IDCardFront = payload.IDCardFront
item.IDCardBack = payload.IDCardBack
item.EducationPhoto = payload.EducationPhoto
item.PoliticalStatus = payload.PoliticalStatus
item.MaritalStatus = payload.MaritalStatus
item.NativePlace = payload.NativePlace
item.HouseholdAddress = payload.HouseholdAddress
item.CurrentAddress = payload.CurrentAddress
item.EmergencyContact = payload.EmergencyContact
item.EmergencyPhone = payload.EmergencyPhone
item.EmergencyRelation = payload.EmergencyRelation
item.WorkEmail = payload.WorkEmail
item.HireDate = strings.TrimSpace(payload.HireDate)
item.RegularDate = strings.TrimSpace(payload.RegularDate)
item.LeaveDate = strings.TrimSpace(payload.LeaveDate)
item.EmploymentStatus = payload.EmploymentStatus
item.Remark = payload.Remark
item.UpdateTime = &now
if _, err := models.Orm.Update(&item,
"FileNo", "IDCard", "IDCardFront", "IDCardBack", "EducationPhoto",
"PoliticalStatus", "MaritalStatus", "NativePlace",
"HouseholdAddress", "CurrentAddress", "EmergencyContact", "EmergencyPhone",
"EmergencyRelation", "WorkEmail", "HireDate", "RegularDate", "LeaveDate",
"EmploymentStatus", "Remark", "UpdateTime"); err != nil {
c.efErr(500, 500, "保存失败")
return
}
c.efOk(item)
}
// Delete DELETE /backend/oa/employeefile/delete/:id 软删除档案。
func (c *BackendEmployeeFileController) Delete() {
claims, err := c.efClaims()
if err != nil {
c.efErr(401, 401, "未登录或无权限")
return
}
tid := claims.TenantId
id, err := strconv.ParseUint(c.Ctx.Input.Param(":id"), 10, 64)
if err != nil || id == 0 {
c.efErr(400, 400, "无效ID")
return
}
now := time.Now()
n, err := c.efQuery(tid).Filter("id", id).
Update(map[string]interface{}{
"IsDeleted": int8(1),
"DeleteTime": now,
"UpdateTime": now,
})
if err != nil || n == 0 {
c.efErr(404, 404, "档案不存在或已删除")
return
}
c.efOk(nil)
}
// FiledEmployees GET /backend/oa/employeefile/filedEmployees
// 返回已建档的员工ID列表,前端用于过滤出可建档(未建档)员工。
func (c *BackendEmployeeFileController) FiledEmployees() {
claims, err := c.efClaims()
if err != nil {
c.efErr(401, 401, "未登录或无权限")
return
}
tid := claims.TenantId
var files []models.BackendEmployeeFile
if _, err := c.efQuery(tid).All(&files, "EmployeeID"); err != nil {
c.efErr(500, 500, "查询失败")
return
}
ids := make([]uint64, 0, len(files))
for _, f := range files {
ids = append(ids, f.EmployeeID)
}
c.efOk(ids)
}
// employeeFileRecordPayload 档案子记录请求体。
// type: 1-教育经历 2-工作经历 3-合同信息 4-证照附件。
type employeeFileRecordPayload struct {
EmployeeID uint64 `json:"employee_id"`
Type int8 `json:"type"`
Title string `json:"title"`
SubTitle string `json:"sub_title"`
Extra string `json:"extra"`
StartDate string `json:"start_date"`
EndDate string `json:"end_date"`
Description string `json:"description"`
AttachmentURL string `json:"attachment_url"`
Sort int `json:"sort"`
}
// efParseRecordPayload 读取并校验子记录请求体;校验失败时直接输出错误响应。
func (c *BackendEmployeeFileController) efParseRecordPayload() (employeeFileRecordPayload, bool) {
var payload employeeFileRecordPayload
raw, err := io.ReadAll(c.Ctx.Request.Body)
if err != nil || json.Unmarshal(raw, &payload) != nil {
c.efErr(400, 400, "参数错误")
return payload, false
}
if payload.EmployeeID == 0 {
c.efErr(400, 400, "参数缺少员工")
return payload, false
}
if payload.Type < 1 || payload.Type > 4 {
c.efErr(400, 400, "记录类型无效")
return payload, false
}
payload.Title = strings.TrimSpace(payload.Title)
if payload.Title == "" {
c.efErr(400, 400, "请输入名称")
return payload, false
}
if len(payload.Title) > 100 {
c.efErr(400, 400, "名称不能超过100字")
return payload, false
}
payload.StartDate = strings.TrimSpace(payload.StartDate)
payload.EndDate = strings.TrimSpace(payload.EndDate)
if !efIsValidDate(payload.StartDate) || !efIsValidDate(payload.EndDate) {
c.efErr(400, 400, "日期格式无效")
return payload, false
}
if payload.StartDate != "" && payload.EndDate != "" && payload.EndDate < payload.StartDate {
c.efErr(400, 400, "结束日期不能早于开始日期")
return payload, false
}
if len(payload.AttachmentURL) > 500 {
c.efErr(400, 400, "附件地址过长")
return payload, false
}
return payload, true
}
// CreateRecord POST /backend/oa/employeefile/record/create 新增子记录。
func (c *BackendEmployeeFileController) CreateRecord() {
claims, err := c.efClaims()
if err != nil {
c.efErr(401, 401, "未登录或无权限")
return
}
tid := claims.TenantId
payload, ok := c.efParseRecordPayload()
if !ok {
return
}
// 档案必须存在(记录挂在员工上,但以建档为前提)
exists, _ := c.efQuery(tid).Filter("employee_id", payload.EmployeeID).Count()
if exists == 0 {
c.efErr(404, 404, "档案不存在,请先建档")
return
}
now := time.Now()
item := &models.BackendEmployeeFileRecord{
Tid: tid,
EmployeeID: payload.EmployeeID,
Type: payload.Type,
Title: payload.Title,
SubTitle: payload.SubTitle,
Extra: payload.Extra,
StartDate: payload.StartDate,
EndDate: payload.EndDate,
Description: payload.Description,
AttachmentURL: payload.AttachmentURL,
Sort: payload.Sort,
IsDeleted: 0,
CreateTime: now,
UpdateTime: &now,
}
if _, err := models.Orm.Insert(item); err != nil {
c.efErr(500, 500, "保存失败")
return
}
c.efOk(item)
}
// UpdateRecord POST /backend/oa/employeefile/record/update/:id 更新子记录。
func (c *BackendEmployeeFileController) UpdateRecord() {
claims, err := c.efClaims()
if err != nil {
c.efErr(401, 401, "未登录或无权限")
return
}
tid := claims.TenantId
id, err := strconv.ParseUint(c.Ctx.Input.Param(":id"), 10, 64)
if err != nil || id == 0 {
c.efErr(400, 400, "无效ID")
return
}
payload, ok := c.efParseRecordPayload()
if !ok {
return
}
var item models.BackendEmployeeFileRecord
if err := models.Orm.QueryTable(new(models.BackendEmployeeFileRecord)).
Filter("tid", tid).
Filter("is_deleted", 0).
Filter("id", id).
One(&item); err != nil {
c.efErr(404, 404, "记录不存在")
return
}
now := time.Now()
item.Type = payload.Type
item.Title = payload.Title
item.SubTitle = payload.SubTitle
item.Extra = payload.Extra
item.StartDate = payload.StartDate
item.EndDate = payload.EndDate
item.Description = payload.Description
item.AttachmentURL = payload.AttachmentURL
item.Sort = payload.Sort
item.UpdateTime = &now
if _, err := models.Orm.Update(&item,
"Type", "Title", "SubTitle", "Extra", "StartDate", "EndDate",
"Description", "AttachmentURL", "Sort", "UpdateTime"); err != nil {
c.efErr(500, 500, "保存失败")
return
}
c.efOk(item)
}
// DeleteRecord DELETE /backend/oa/employeefile/record/delete/:id 软删除子记录。
func (c *BackendEmployeeFileController) DeleteRecord() {
claims, err := c.efClaims()
if err != nil {
c.efErr(401, 401, "未登录或无权限")
return
}
tid := claims.TenantId
id, err := strconv.ParseUint(c.Ctx.Input.Param(":id"), 10, 64)
if err != nil || id == 0 {
c.efErr(400, 400, "无效ID")
return
}
now := time.Now()
n, err := models.Orm.QueryTable(new(models.BackendEmployeeFileRecord)).
Filter("tid", tid).
Filter("is_deleted", 0).
Filter("id", id).
Update(map[string]interface{}{
"IsDeleted": int8(1),
"DeleteTime": now,
"UpdateTime": now,
})
if err != nil || n == 0 {
c.efErr(404, 404, "记录不存在或已删除")
return
}
c.efOk(nil)
}