Files
iotlcm/server/internal/logic/app/app_message.go
T
MiFaZhan 2ed5dfa6a1 chore: 初始开源发布
iotlcm-app:小程序端 + 用户端服务。

- app/:uni-app 小程序前端(基于 unibest,MIT)
  - 微信 AppID → YOUR_WX_APPID,uni-app AppID → __UNI__YOURAPPID
  - 内网后端地址 → admin.iotlcm.example.com
  - 应用标题与 unibest 模板 README 重写(保留上游致谢与 MIT 声明)
- server/:GoFrame 用户端服务(基于 XiuAdmin,Apache-2.0)
  - module xiuadmin → git.5iot.com/iotlcm-public/iotlcm-app(360 文件)
  - 支付回调地址 → app.iotlcm.example.com,商户名与应用名泛化
  - 真实手机号 → 合法 11 位编造号(保留号段前 3 位 + 后 4 位)
  - 依赖号码尾部的断言同步更新
  - 补 server/LICENSE(Apache-2.0)
- 移除内部资产:CI 触发器
- 补根 README,LF 行尾 + .gitattributes

已知:TestSysDept_RefreshDeptAncestors 在源仓同一提交上同样失败,属既有缺陷。
2026-10-26 14:01:25 +08:00

198 lines
5.8 KiB
Go

package app
import (
"context"
"strings"
"git.5iot.com/iotlcm-public/iotlcm-app/internal/dao"
"git.5iot.com/iotlcm-public/iotlcm-app/internal/model"
"git.5iot.com/iotlcm-public/iotlcm-app/internal/model/do"
"git.5iot.com/iotlcm-public/iotlcm-app/internal/model/entity"
"git.5iot.com/iotlcm-public/iotlcm-app/internal/service"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/os/gtime"
)
type sAppMessage struct{}
func NewAppMessage() *sAppMessage {
return &sAppMessage{}
}
func init() {
service.RegisterAppMessage(NewAppMessage())
}
func (s *sAppMessage) List(ctx context.Context, param *model.AppMessageListParam) (items []*model.AppMessageModel, total int, err error) {
if param == nil {
param = &model.AppMessageListParam{}
}
if param.Page <= 0 {
param.Page = 1
}
if param.PageSize <= 0 {
param.PageSize = 20
}
if param.PageSize > 100 {
param.PageSize = 100
}
user, err := appCurrentUser(ctx)
if err != nil {
return nil, 0, err
}
mod := s.applyFilters(s.ownerMessageModel(ctx, user), param)
total, err = mod.Count()
if err != nil {
return nil, 0, gerror.Wrap(err, "查询消息数量失败")
}
if total == 0 {
return []*model.AppMessageModel{}, 0, nil
}
if err = mod.
Fields(appMessageFields()).
Page(param.Page, param.PageSize).
OrderDesc("COALESCE(ml." + dao.TMessageLog.Columns().SendTime + ", um." + dao.TUserMessage.Columns().CreatedAt + ")").
OrderDesc("um." + dao.TUserMessage.Columns().MessageId).
Scan(&items); err != nil {
return nil, 0, gerror.Wrap(err, "查询消息列表失败")
}
for _, item := range items {
fillAppMessageText(item)
}
return items, total, nil
}
func (s *sAppMessage) Detail(ctx context.Context, param *model.AppMessageDetailParam) (*model.AppMessageModel, error) {
if param == nil || param.MessageId == 0 {
return nil, gerror.New("消息ID不能为空")
}
user, err := appCurrentUser(ctx)
if err != nil {
return nil, err
}
var item *model.AppMessageModel
if err = s.ownerMessageModel(ctx, user).
Fields(appMessageFields()).
Where("um."+dao.TUserMessage.Columns().MessageId, param.MessageId).
Limit(1).
Scan(&item); err != nil {
return nil, gerror.Wrap(err, "查询消息详情失败")
}
if item == nil || item.MessageId == 0 {
return nil, gerror.New("消息不存在或无权查看")
}
fillAppMessageText(item)
if item.IsRead == 0 {
if err = s.Read(ctx, &model.AppMessageReadParam{Ids: []uint64{item.MessageId}}); err != nil {
return nil, err
}
item.IsRead = 1
}
return item, nil
}
func (s *sAppMessage) Read(ctx context.Context, param *model.AppMessageReadParam) error {
if param == nil || len(param.Ids) == 0 {
return gerror.New("消息ID不能为空")
}
user, err := appCurrentUser(ctx)
if err != nil {
return err
}
_, err = dao.TUserMessage.Ctx(ctx).
Where(dao.TUserMessage.Columns().TenantId, user.TenantId).
Where(dao.TUserMessage.Columns().UserId, user.UserId).
WhereIn(dao.TUserMessage.Columns().MessageId, param.Ids).
Where(dao.TUserMessage.Columns().IsRead, 0).
Data(do.TUserMessage{
IsRead: 1,
UpdatedBy: user.UserId,
UpdatedAt: gtime.Now(),
}).
Update()
if err != nil {
return gerror.Wrap(err, "标记消息已读失败")
}
return nil
}
func (s *sAppMessage) ownerMessageModel(ctx context.Context, user *entity.SysUser) *gdb.Model {
return dao.TUserMessage.Ctx(ctx).As("um").
LeftJoin(dao.TMessageLog.Table()+" ml", "ml."+dao.TMessageLog.Columns().MessageLogId+" = um."+dao.TUserMessage.Columns().MessageLogId).
Where("um."+dao.TUserMessage.Columns().TenantId, user.TenantId).
Where("um."+dao.TUserMessage.Columns().UserId, user.UserId).
Where("(ml."+dao.TMessageLog.Columns().MessageLogId+" IS NULL OR ml."+dao.TMessageLog.Columns().IsRecall+" = ?)", 0)
}
func (s *sAppMessage) applyFilters(mod *gdb.Model, param *model.AppMessageListParam) *gdb.Model {
if param.IsRead != nil {
mod = mod.Where("um."+dao.TUserMessage.Columns().IsRead, *param.IsRead)
}
if param.MessageType != nil && *param.MessageType > 0 {
mod = mod.Where("ml."+dao.TMessageLog.Columns().MessageType, *param.MessageType)
}
keyword := strings.TrimSpace(param.Keyword)
if keyword != "" {
like := "%" + keyword + "%"
mod = mod.Where(
"(ml."+dao.TMessageLog.Columns().MessageTitle+" LIKE ? OR ml."+dao.TMessageLog.Columns().MessageContent+" LIKE ? OR ml."+dao.TMessageLog.Columns().MessageLink+" LIKE ?)",
like, like, like,
)
}
return mod
}
func appMessageFields() string {
return strings.Join([]string{
"um." + dao.TUserMessage.Columns().MessageId + " AS message_id",
"um." + dao.TUserMessage.Columns().MessageId + " AS id",
"um." + dao.TUserMessage.Columns().MessageLogId + " AS message_log_id",
"COALESCE(ml." + dao.TMessageLog.Columns().MessageType + ", 1) AS message_type",
"COALESCE(ml." + dao.TMessageLog.Columns().MessageTitle + ", '') AS title",
"COALESCE(ml." + dao.TMessageLog.Columns().MessageContent + ", '') AS content",
"COALESCE(ml." + dao.TMessageLog.Columns().MessageLink + ", '') AS link",
"um." + dao.TUserMessage.Columns().IsRead + " AS is_read",
"um." + dao.TUserMessage.Columns().CreatedAt + " AS created_at",
"um." + dao.TUserMessage.Columns().UpdatedAt + " AS updated_at",
"COALESCE(ml." + dao.TMessageLog.Columns().SendTime + ", um." + dao.TUserMessage.Columns().CreatedAt + ") AS send_time",
}, ", ")
}
func fillAppMessageText(item *model.AppMessageModel) {
if item == nil {
return
}
item.Id = item.MessageId
if strings.TrimSpace(item.Title) == "" {
item.Title = "未命名消息"
}
item.MessageTypeText = appMessageTypeText(item.MessageType)
}
func appMessageTypeText(messageType int) string {
switch messageType {
case 1:
return "系统通知"
case 2:
return "平台公告"
case 3:
return "订单通知"
case 4:
return "账户通知"
default:
return "消息"
}
}