307 lines
13 KiB
Go
307 lines
13 KiB
Go
package store
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type AdminWorkbenchModule struct {
|
|
ID int64 `db:"id"`
|
|
Code string `db:"code"`
|
|
Name string `db:"name"`
|
|
IconFileID string `db:"icon_file_id"`
|
|
Route string `db:"route"`
|
|
SortOrder int `db:"sort_order"`
|
|
UnreadKey string `db:"unread_key"`
|
|
Enabled int `db:"enabled"`
|
|
Permission string `db:"permission"`
|
|
CreatedAt time.Time `db:"created_at"`
|
|
UpdatedAt time.Time `db:"updated_at"`
|
|
}
|
|
|
|
type AdminWorkbenchModuleInput struct {
|
|
ID int64
|
|
Code string
|
|
Name string
|
|
IconFileID string
|
|
Route string
|
|
SortOrder int
|
|
UnreadKey string
|
|
Enabled int
|
|
Permission string
|
|
}
|
|
|
|
type AdminWorkbenchReorderItem struct {
|
|
Code string
|
|
SortOrder int
|
|
}
|
|
|
|
type AdminMailboxConfig struct {
|
|
ID int64 `db:"id"`
|
|
Enabled int `db:"enabled"`
|
|
AllowAnonymous int `db:"allow_anonymous"`
|
|
RequiredFields string `db:"required_fields"`
|
|
MaxLength int `db:"max_length"`
|
|
AttachmentPolicy string `db:"attachment_policy"`
|
|
NoticeTitle string `db:"notice_title"`
|
|
NoticeContent string `db:"notice_content"`
|
|
NoticeUpdatedAt time.Time `db:"notice_updated_at"`
|
|
UpdatedAt time.Time `db:"updated_at"`
|
|
}
|
|
|
|
type AdminFeedbackConfig struct {
|
|
ID int64 `db:"id"`
|
|
Categories string `db:"categories"`
|
|
MaxLength int `db:"max_length"`
|
|
AttachmentPolicy string `db:"attachment_policy"`
|
|
AllowContact int `db:"allow_contact"`
|
|
DiagnosticsNotice string `db:"diagnostics_notice"`
|
|
UpdatedAt time.Time `db:"updated_at"`
|
|
}
|
|
|
|
func (s *Store) AdminWorkbenchModules(ctx context.Context, page, perPage int, code, name string, enabled *int) ([]AdminWorkbenchModule, int, error) {
|
|
if page <= 0 {
|
|
page = 1
|
|
}
|
|
if perPage <= 0 {
|
|
perPage = 20
|
|
}
|
|
if perPage > 200 {
|
|
perPage = 200
|
|
}
|
|
where := []string{"1=1"}
|
|
args := []any{}
|
|
if code = strings.TrimSpace(code); code != "" {
|
|
where = append(where, "code LIKE ?")
|
|
args = append(args, "%"+code+"%")
|
|
}
|
|
if name = strings.TrimSpace(name); name != "" {
|
|
where = append(where, "name LIKE ?")
|
|
args = append(args, "%"+name+"%")
|
|
}
|
|
if enabled != nil {
|
|
where = append(where, "enabled=?")
|
|
args = append(args, *enabled)
|
|
}
|
|
whereSQL := strings.Join(where, " AND ")
|
|
var total int
|
|
if err := s.db.GetContext(ctx, &total, "SELECT COUNT(*) FROM im_workbench_apps WHERE "+whereSQL, args...); err != nil {
|
|
return nil, 0, err
|
|
}
|
|
queryArgs := append([]any{}, args...)
|
|
queryArgs = append(queryArgs, (page-1)*perPage, perPage)
|
|
var items []AdminWorkbenchModule
|
|
err := s.db.SelectContext(ctx, &items, `SELECT id,code,name,IFNULL(icon_file_id,'') AS icon_file_id,route,sort_order,IFNULL(unread_key,'') AS unread_key,enabled+0 AS enabled,IFNULL(permission,'enabled') AS permission,created_at,updated_at
|
|
FROM im_workbench_apps WHERE `+whereSQL+` ORDER BY sort_order ASC,id ASC LIMIT ?,?`, queryArgs...)
|
|
return items, total, err
|
|
}
|
|
|
|
func (s *Store) UpsertAdminWorkbenchModule(ctx context.Context, in AdminWorkbenchModuleInput) (*AdminWorkbenchModule, error) {
|
|
now := time.Now().UTC().Round(time.Millisecond)
|
|
in.Code = strings.TrimSpace(in.Code)
|
|
in.Name = strings.TrimSpace(in.Name)
|
|
in.IconFileID = strings.TrimSpace(in.IconFileID)
|
|
in.Route = strings.TrimSpace(in.Route)
|
|
in.UnreadKey = strings.TrimSpace(in.UnreadKey)
|
|
in.Permission = strings.TrimSpace(in.Permission)
|
|
if in.Permission == "" {
|
|
in.Permission = "enabled"
|
|
}
|
|
if in.ID > 0 {
|
|
res, err := s.db.ExecContext(ctx, `UPDATE im_workbench_apps SET code=?,name=?,icon_file_id=?,route=?,sort_order=?,unread_key=?,enabled=?,permission=?,updated_at=? WHERE id=?`,
|
|
in.Code, in.Name, in.IconFileID, in.Route, in.SortOrder, in.UnreadKey, in.Enabled, in.Permission, now, in.ID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if rows, _ := res.RowsAffected(); rows == 0 {
|
|
return nil, ErrNotFound
|
|
}
|
|
return s.adminWorkbenchModuleByID(ctx, in.ID)
|
|
}
|
|
res, err := s.db.ExecContext(ctx, `INSERT INTO im_workbench_apps(code,name,icon_file_id,route,sort_order,unread_key,enabled,permission,created_at,updated_at)
|
|
VALUES(?,?,?,?,?,?,?,?,?,?)`, in.Code, in.Name, in.IconFileID, in.Route, in.SortOrder, in.UnreadKey, in.Enabled, in.Permission, now, now)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
id, _ := res.LastInsertId()
|
|
return s.adminWorkbenchModuleByID(ctx, id)
|
|
}
|
|
|
|
func (s *Store) DisableAdminWorkbenchModule(ctx context.Context, id int64, code string) error {
|
|
now := time.Now().UTC().Round(time.Millisecond)
|
|
var res sql.Result
|
|
var err error
|
|
if id > 0 {
|
|
res, err = s.db.ExecContext(ctx, `UPDATE im_workbench_apps SET enabled=0,updated_at=? WHERE id=?`, now, id)
|
|
} else {
|
|
res, err = s.db.ExecContext(ctx, `UPDATE im_workbench_apps SET enabled=0,updated_at=? WHERE code=?`, now, strings.TrimSpace(code))
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if rows, _ := res.RowsAffected(); rows == 0 {
|
|
return ErrNotFound
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *Store) ReorderAdminWorkbenchModules(ctx context.Context, items []AdminWorkbenchReorderItem) error {
|
|
tx, err := s.db.BeginTxx(ctx, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer func() { _ = tx.Rollback() }()
|
|
now := time.Now().UTC().Round(time.Millisecond)
|
|
for _, item := range items {
|
|
if _, err = tx.ExecContext(ctx, `UPDATE im_workbench_apps SET sort_order=?,updated_at=? WHERE code=?`, item.SortOrder, now, strings.TrimSpace(item.Code)); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return tx.Commit()
|
|
}
|
|
|
|
func (s *Store) AdminOrganizationDepartments(ctx context.Context, page, perPage int, parentID, name, state string) ([]OrganizationDepartment, int, error) {
|
|
page, perPage = normalizeAdminPage(page, perPage)
|
|
where := []string{"1=1"}
|
|
args := []any{}
|
|
if parentID = strings.TrimSpace(parentID); parentID != "" {
|
|
where = append(where, "parent_id=?")
|
|
args = append(args, parentID)
|
|
}
|
|
if name = strings.TrimSpace(name); name != "" {
|
|
where = append(where, "name LIKE ?")
|
|
args = append(args, "%"+name+"%")
|
|
}
|
|
if state = strings.TrimSpace(state); state != "" {
|
|
where = append(where, "state=?")
|
|
args = append(args, state)
|
|
}
|
|
whereSQL := strings.Join(where, " AND ")
|
|
var total int
|
|
if err := s.db.GetContext(ctx, &total, "SELECT COUNT(*) FROM im_org_departments WHERE "+whereSQL, args...); err != nil {
|
|
return nil, 0, err
|
|
}
|
|
queryArgs := append([]any{}, args...)
|
|
queryArgs = append(queryArgs, (page-1)*perPage, perPage)
|
|
var items []OrganizationDepartment
|
|
err := s.db.SelectContext(ctx, &items, `SELECT d.department_id,d.name,d.parent_id,
|
|
(SELECT COUNT(*) FROM im_org_departments c WHERE c.parent_id=d.department_id AND c.state='active') AS child_count,
|
|
(SELECT COUNT(*) FROM im_org_members m WHERE m.department_id=d.department_id AND m.employment_state='active') AS member_count,
|
|
d.version
|
|
FROM im_org_departments d WHERE `+whereSQL+` ORDER BY d.parent_id ASC,d.version DESC,d.department_id ASC LIMIT ?,?`, queryArgs...)
|
|
return items, total, err
|
|
}
|
|
|
|
func (s *Store) AdminOrganizationMembers(ctx context.Context, page, perPage int, departmentID, keyword, state string, userID int64) ([]OrganizationMember, int, error) {
|
|
page, perPage = normalizeAdminPage(page, perPage)
|
|
where := []string{"1=1"}
|
|
args := []any{}
|
|
if departmentID = strings.TrimSpace(departmentID); departmentID != "" {
|
|
where = append(where, "department_id=?")
|
|
args = append(args, departmentID)
|
|
}
|
|
if keyword = strings.TrimSpace(keyword); keyword != "" {
|
|
where = append(where, "(display_name LIKE ? OR position LIKE ? OR mobile LIKE ? OR email LIKE ?)")
|
|
like := "%" + keyword + "%"
|
|
args = append(args, like, like, like, like)
|
|
}
|
|
if state = strings.TrimSpace(state); state != "" {
|
|
where = append(where, "employment_state=?")
|
|
args = append(args, state)
|
|
}
|
|
if userID > 0 {
|
|
where = append(where, "user_id=?")
|
|
args = append(args, userID)
|
|
}
|
|
whereSQL := strings.Join(where, " AND ")
|
|
var total int
|
|
if err := s.db.GetContext(ctx, &total, "SELECT COUNT(*) FROM im_org_members WHERE "+whereSQL, args...); err != nil {
|
|
return nil, 0, err
|
|
}
|
|
queryArgs := append([]any{}, args...)
|
|
queryArgs = append(queryArgs, (page-1)*perPage, perPage)
|
|
var items []OrganizationMember
|
|
err := s.db.SelectContext(ctx, &items, `SELECT user_id,department_id,display_name,position,IFNULL(mobile,'') AS mobile,IFNULL(email,'') AS email,
|
|
IFNULL(avatar_file_id,'') AS avatar_file_id,employment_state AS status,contactable,version
|
|
FROM im_org_members WHERE `+whereSQL+` ORDER BY department_id ASC,user_id ASC LIMIT ?,?`, queryArgs...)
|
|
return items, total, err
|
|
}
|
|
|
|
func (s *Store) AdminMailboxConfig(ctx context.Context) (*AdminMailboxConfig, error) {
|
|
var cfg AdminMailboxConfig
|
|
if err := s.db.GetContext(ctx, &cfg, `SELECT id,enabled+0 AS enabled,allow_anonymous+0 AS allow_anonymous,CAST(required_fields AS CHAR) AS required_fields,max_length,CAST(attachment_policy AS CHAR) AS attachment_policy,notice_title,notice_content,notice_updated_at,updated_at FROM im_mailbox_config WHERE id=1`); err != nil {
|
|
if errorsIsNoRows(err) {
|
|
now := time.Now().UTC().Round(time.Millisecond)
|
|
return &AdminMailboxConfig{ID: 1, Enabled: 1, AllowAnonymous: 1, RequiredFields: `["subject","content"]`, MaxLength: 5000, AttachmentPolicy: `{"max_files":5,"purpose":"executive_mailbox"}`, NoticeTitle: "总经理信箱", NoticeContent: "请如实填写诉求,匿名提交将按企业政策保护展示身份。", NoticeUpdatedAt: now, UpdatedAt: now}, nil
|
|
}
|
|
return nil, err
|
|
}
|
|
return &cfg, nil
|
|
}
|
|
|
|
func (s *Store) UpdateAdminMailboxConfig(ctx context.Context, cfg AdminMailboxConfig) error {
|
|
now := time.Now().UTC().Round(time.Millisecond)
|
|
_, err := s.db.ExecContext(ctx, `INSERT INTO im_mailbox_config(id,enabled,allow_anonymous,required_fields,max_length,attachment_policy,notice_title,notice_content,notice_updated_at,created_at,updated_at)
|
|
VALUES(1,?,?,?,?,?,?,?,?,?,?)
|
|
ON DUPLICATE KEY UPDATE enabled=VALUES(enabled),allow_anonymous=VALUES(allow_anonymous),required_fields=VALUES(required_fields),max_length=VALUES(max_length),attachment_policy=VALUES(attachment_policy),notice_title=VALUES(notice_title),notice_content=VALUES(notice_content),notice_updated_at=VALUES(notice_updated_at),updated_at=VALUES(updated_at)`,
|
|
cfg.Enabled, cfg.AllowAnonymous, defaultString(cfg.RequiredFields, `["subject","content"]`), cfg.MaxLength, defaultString(cfg.AttachmentPolicy, `{"max_files":5,"purpose":"executive_mailbox"}`), cfg.NoticeTitle, cfg.NoticeContent, now, now, now)
|
|
return err
|
|
}
|
|
|
|
func (s *Store) AdminFeedbackConfig(ctx context.Context) (*AdminFeedbackConfig, error) {
|
|
var cfg AdminFeedbackConfig
|
|
if err := s.db.GetContext(ctx, &cfg, `SELECT id,CAST(categories AS CHAR) AS categories,max_length,CAST(attachment_policy AS CHAR) AS attachment_policy,allow_contact+0 AS allow_contact,diagnostics_notice,updated_at FROM im_feedback_config WHERE id=1`); err != nil {
|
|
if errorsIsNoRows(err) {
|
|
return &AdminFeedbackConfig{ID: 1, Categories: `["bug","suggestion","complaint","other"]`, MaxLength: 1000, AttachmentPolicy: `{"max_files":5,"purpose":"feedback"}`, AllowContact: 1, DiagnosticsNotice: "仅在你同意时上传脱敏诊断信息。", UpdatedAt: time.Now().UTC().Round(time.Millisecond)}, nil
|
|
}
|
|
return nil, err
|
|
}
|
|
return &cfg, nil
|
|
}
|
|
|
|
func (s *Store) UpdateAdminFeedbackConfig(ctx context.Context, cfg AdminFeedbackConfig) error {
|
|
now := time.Now().UTC().Round(time.Millisecond)
|
|
_, err := s.db.ExecContext(ctx, `INSERT INTO im_feedback_config(id,categories,max_length,attachment_policy,allow_contact,diagnostics_notice,created_at,updated_at)
|
|
VALUES(1,?,?,?,?,?,?,?)
|
|
ON DUPLICATE KEY UPDATE categories=VALUES(categories),max_length=VALUES(max_length),attachment_policy=VALUES(attachment_policy),allow_contact=VALUES(allow_contact),diagnostics_notice=VALUES(diagnostics_notice),updated_at=VALUES(updated_at)`,
|
|
defaultString(cfg.Categories, `["bug","suggestion","complaint","other"]`), cfg.MaxLength, defaultString(cfg.AttachmentPolicy, `{"max_files":5,"purpose":"feedback"}`), cfg.AllowContact, cfg.DiagnosticsNotice, now, now)
|
|
return err
|
|
}
|
|
|
|
func (s *Store) adminWorkbenchModuleByID(ctx context.Context, id int64) (*AdminWorkbenchModule, error) {
|
|
var item AdminWorkbenchModule
|
|
if err := s.db.GetContext(ctx, &item, `SELECT id,code,name,IFNULL(icon_file_id,'') AS icon_file_id,route,sort_order,IFNULL(unread_key,'') AS unread_key,enabled+0 AS enabled,IFNULL(permission,'enabled') AS permission,created_at,updated_at FROM im_workbench_apps WHERE id=?`, id); err != nil {
|
|
if errorsIsNoRows(err) {
|
|
return nil, ErrNotFound
|
|
}
|
|
return nil, err
|
|
}
|
|
return &item, nil
|
|
}
|
|
|
|
func errorsIsNoRows(err error) bool {
|
|
return err == sql.ErrNoRows
|
|
}
|
|
|
|
func defaultString(value string, fallback string) string {
|
|
if strings.TrimSpace(value) == "" {
|
|
return fallback
|
|
}
|
|
return value
|
|
}
|
|
|
|
func normalizeAdminPage(page, perPage int) (int, int) {
|
|
if page <= 0 {
|
|
page = 1
|
|
}
|
|
if perPage <= 0 {
|
|
perPage = 20
|
|
}
|
|
if perPage > 200 {
|
|
perPage = 200
|
|
}
|
|
return page, perPage
|
|
}
|