Files
iotlcm/server/internal/logic/common/t_message_log.go
T
MiFaZhan 59eb34976a chore: 初始开源发布
iotlcm-admin-svr:管理后台服务。

- Go module:iotlcm → git.5iot.com/iotlcm-public/iotlcm-admin-svr(729 文件)
- 凭据与地址脱敏:JWT signingKey(含代码里的 MustGet 默认值)、
  MySQL DSN、Redis 口令、告警邮件发件人邮箱全部改为占位;
  私网 IP → 文档地址段;库名 xiujie_admin → iotlcm
- 真实手机号/邮箱 → 合法 11 位编造号 / example.com
- 移除内部资产:CI 触发器、过时 DDL(sql/db_iotlcm.sql)、
  私有依赖 go get 行、明文 registry 登录口令
- 启动 banner 统一 IOTLCM 方块字 + 社区惯例署名
- Dockerfile 改用公共基础镜像
- 拼错的 RADEME.md → README.md,README 重写
- 需要真实数据库的用例改为可用 IOTLCM_TEST_WITH_DB=1 显式启用
- 补 Apache-2.0 LICENSE,LF 行尾 + .gitattributes

数据库 schema 不随本仓发布(生产 DDL 已过时),README 已说明。
2026-10-23 14:48:30 +08:00

324 lines
10 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 gen
// @Link https://github.com/xiujiecn/xiu-admin
// @Copyright Copyright (c) 2026 LiXiujie
// @Author Lxj <li@xiujie.cn>
// @License https://github.com/xiujiecn/xiu-admin/blob/master/LICENSE
// @AutoGenerate Version
package common
import (
"context"
"fmt"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/dao"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/contexts"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/websocket"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/xgorm/handler"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/xgorm/hook"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model/entity"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/service"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/utility/convert"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/utility/excel"
"strings"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gctx"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/util/gconv"
)
type sCommonTMessageLog struct{}
func NewCommonTMessageLog() *sCommonTMessageLog {
return &sCommonTMessageLog{}
}
func init() {
service.RegisterCommonTMessageLog(NewCommonTMessageLog())
}
// Model 消息日志ORM模型
func (s *sCommonTMessageLog) Model(ctx context.Context, option ...*handler.Option) *gdb.Model {
if len(option) == 0 {
option = append(option, &handler.Option{
FilterTenant: true,
FilterAuth: true,
})
}
return handler.Model(dao.TMessageLog.Ctx(ctx), option...)
}
// List 获取消息日志列表
func (s *sCommonTMessageLog) List(ctx context.Context, in *model.TMessageLogListParam) (list []*model.TMessageLogListModel, totalCount int, err error) {
mod := dao.TMessageLog.Ctx(ctx)
deptId := contexts.GetDeptId(ctx)
// 字段过滤
mod = mod.Fields(model.TMessageLogListModel{})
g.Log().Info(ctx, "查询用户ID:", contexts.GetUserId(ctx))
if in.MessageType != nil {
mod = mod.Where(dao.TMessageLog.Columns().MessageType, in.MessageType)
}
if title := strings.TrimSpace(in.MessageTitle); title != "" {
mod = mod.WhereLike(dao.TMessageLog.Columns().MessageTitle, "%"+title+"%")
}
// 查询发送时间范围
if in.SendTimeStart != nil {
mod = mod.WhereGTE(dao.TMessageLog.Columns().SendTime, in.SendTimeStart)
}
if in.SendTimeEnd != nil {
mod = mod.WhereLTE(dao.TMessageLog.Columns().SendTime, in.SendTimeEnd.EndOfDay())
}
// 兼容旧的单日发送时间查询
if in.SendTime != nil {
mod = mod.Where(fmt.Sprintf("DATE(%s) = ?", dao.TMessageLog.Columns().SendTime), in.SendTime.Format("Y-m-d"))
}
// 查询用户ID(JSON字段,使用JSON_CONTAINS查询)
userId := contexts.GetUserId(ctx)
var sysUser *entity.SysUser
dao.SysUser.Ctx(ctx).WherePri(userId).Scan(&sysUser)
userType := sysUser.UserType
// 1. 查找顶级部门(ParentId = 0)
var topDept entity.SysDept
err = dao.SysDept.Ctx(ctx).
Where(dao.SysDept.Columns().ParentId, 0).
Scan(&topDept)
if err != nil {
return
}
// 检查顶级部门是否存在
if topDept.DeptId > 0 && deptId != topDept.DeptId {
mod = mod.Where(dao.TMessageLog.Columns().IsRecall, 0)
mod = mod.Where(fmt.Sprintf("JSON_CONTAINS(%s, CAST(? AS JSON))", dao.TMessageLog.Columns().UserIds), userId)
if userType == "sys_user" {
mod = mod.Where(dao.TMessageLog.Columns().SendUserType, 1)
} else if userType == "app_user" {
mod = mod.Where(dao.TMessageLog.Columns().SendUserType, 2)
}
}
// 分页
mod = mod.Page(in.Page, in.PageSize)
// 排序
mod = mod.OrderDesc(dao.TMessageLog.Columns().MessageLogId)
// 操作人摘要信息
mod = mod.Hook(hook.MemberSummary)
// 查询数据
if err = mod.ScanAndCount(&list, &totalCount, false); err != nil {
err = gerror.Wrap(err, "获取消息日志列表失败,请稍后重试!")
return
}
return
}
// Export 导出消息日志
func (s *sCommonTMessageLog) Export(ctx context.Context, in *model.TMessageLogListParam) (err error) {
list, totalCount, err := s.List(ctx, in)
if err != nil {
return
}
// 字段的排序是依据tags的字段顺序,如果你不想使用默认的排序方式,可以直接定义 tags = []string{"字段名称", "字段名称2", ...}
tags, err := convert.GetEntityDescTags(model.TMessageLogExportModel{})
if err != nil {
return
}
var (
fileName = "导出消息日志-" + gctx.CtxId(ctx)
sheetName = fmt.Sprintf("索引条件共%v行,共%v页,当前导出是第%v页,本页共%v行", totalCount, (totalCount+in.PageSize-1)/in.PageSize, in.Page, len(list))
exports []model.TMessageLogExportModel
)
if err = gconv.Scan(list, &exports); err != nil {
return
}
err = excel.ExportByStructs(ctx, tags, exports, fileName, sheetName)
return
}
// Edit 修改/新增消息日志
func (s *sCommonTMessageLog) Edit(ctx context.Context, in *model.TMessageLogEditParam) (err error) {
var (
pushUserIds []uint64
pushPayload g.Map
)
err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) {
// 修改
if in.MessageLogId > 0 {
tenantId := contexts.GetTenantId(ctx)
in.TenantId = &tenantId
updatedBy := contexts.GetUserId(ctx)
in.UpdatedBy = &updatedBy
in.UpdatedAt = gtime.Now()
if _, err = s.Model(ctx).
Fields(model.TMessageLogUpdateFields{}).
WherePri(in.MessageLogId).Data(in).Update(); err != nil {
err = gerror.Wrap(err, "修改消息日志失败,请稍后重试!")
}
return
}
// 新增
tenantId := contexts.GetTenantId(ctx)
in.TenantId = &tenantId
createdDept := contexts.GetDeptId(ctx)
in.CreatedDept = &createdDept
createdBy := contexts.GetUserId(ctx)
in.CreatedBy = &createdBy
in.CreatedAt = gtime.Now()
in.SendTime = gtime.Now()
result, err := tx.Ctx(ctx).Model(dao.TMessageLog.Table()).
Fields(model.TMessageLogInsertFields{}).
Data(in).Insert()
if err != nil {
err = gerror.Wrap(err, "新增消息日志失败,请稍后重试!")
return
}
// 获取插入的消息日志ID
messageLogId, err := result.LastInsertId()
if err != nil {
err = gerror.Wrap(err, "获取消息日志ID失败")
return
}
pushPayload = g.Map{
"messageLogId": messageLogId,
"messageType": gconv.Uint(in.MessageType),
"sendUserType": gconv.Uint(in.SendUserType),
"messageTitle": gconv.String(in.MessageTitle),
"messageContent": gconv.String(in.MessageContent),
"messageLink": gconv.String(in.MessageLink),
"sendTime": in.SendTime,
"isRecall": gconv.Uint(in.IsRecall),
"isRead": 0,
}
optionalUserMessageColumns := s.userMessageOptionalColumns(ctx)
// 根据 user_ids 往用户消息表插入记录
if in.UserIds != nil && len(in.UserIds.Interfaces()) > 0 {
userIds := in.UserIds.Interfaces()
for _, userId := range userIds {
userIdInt := gconv.Int64(userId)
if userIdInt <= 0 {
continue
}
userMessageData := g.Map{
dao.TUserMessage.Columns().MessageLogId: messageLogId,
dao.TUserMessage.Columns().UserId: userIdInt,
dao.TUserMessage.Columns().TenantId: tenantId,
dao.TUserMessage.Columns().CreatedDept: createdDept,
dao.TUserMessage.Columns().CreatedBy: createdBy,
dao.TUserMessage.Columns().CreatedAt: in.CreatedAt,
}
if _, ok := optionalUserMessageColumns["user_rank"]; ok {
userMessageData["user_rank"] = 0
}
if _, ok := optionalUserMessageColumns["title"]; ok {
userMessageData["title"] = messageLogStringValue(in.MessageTitle)
}
if _, ok := optionalUserMessageColumns["content"]; ok {
userMessageData["content"] = messageLogStringValue(in.MessageContent)
}
if _, ok := optionalUserMessageColumns["link"]; ok {
userMessageData["link"] = messageLogStringValue(in.MessageLink)
}
_, err = tx.Ctx(ctx).Model(dao.TUserMessage.Table()).Data(userMessageData).Insert()
if err != nil {
err = gerror.Wrap(err, "新增用户消息失败,请稍后重试!")
return
}
pushUserIds = append(pushUserIds, uint64(userIdInt))
}
}
return
})
if err != nil {
return
}
for _, userId := range pushUserIds {
g.Log().Info(ctx, "推送消息到用户 %v", userId)
websocket.SendToUser(userId, &websocket.WResponse{
Event: "message:new",
Data: pushPayload,
})
}
return
}
func (s *sCommonTMessageLog) userMessageOptionalColumns(ctx context.Context) map[string]struct{} {
result, err := g.DB().GetAll(ctx, "SHOW COLUMNS FROM "+dao.TUserMessage.Table())
if err != nil {
return map[string]struct{}{}
}
columns := make(map[string]struct{}, len(result))
for _, row := range result {
field := row["Field"].String()
if field != "" {
columns[field] = struct{}{}
}
}
return columns
}
func messageLogStringValue(value *string) string {
if value == nil {
return ""
}
return *value
}
// Delete 删除消息日志
func (s *sCommonTMessageLog) Delete(ctx context.Context, in *model.TMessageLogDeleteParam) (err error) {
if _, err = s.Model(ctx).WherePri(in.MessageLogId).Unscoped().Delete(); err != nil {
err = gerror.Wrap(err, "删除消息日志失败,请稍后重试!")
return
}
return
}
// View 获取消息日志指定信息,并标记已读
func (s *sCommonTMessageLog) View(ctx context.Context, in *model.TMessageLogViewParam) (res *model.TMessageLogViewModel, err error) {
if err = dao.TMessageLog.Ctx(ctx).WherePri(in.MessageLogId).Hook(hook.MemberSummary).Scan(&res); err != nil {
err = gerror.Wrap(err, "获取消息日志信息,请稍后重试!")
return
}
// 标记用户消息为已读
userId := contexts.GetUserId(ctx)
now := gtime.Now()
_, err = dao.TUserMessage.Ctx(ctx).Where(g.Map{
dao.TUserMessage.Columns().MessageLogId: in.MessageLogId,
dao.TUserMessage.Columns().UserId: userId,
dao.TUserMessage.Columns().IsRead: 0,
}).Data(g.Map{
dao.TUserMessage.Columns().IsRead: 1,
dao.TUserMessage.Columns().UpdatedBy: userId,
dao.TUserMessage.Columns().UpdatedAt: now,
}).Update()
if err != nil {
// 已读标记失败不影响主业务,仅记录日志
g.Log().Warning(ctx, "标记消息已读失败: %v", err)
err = nil
}
return
}