七月迭代短暂完结,还有很多后端的关键东西没有弄,这是一版赶时间做的东西
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 8m26s

This commit is contained in:
2026-07-25 17:06:58 +08:00
parent ad9f613dd6
commit 73f5125d3d
249 changed files with 17137 additions and 877 deletions

View File

@@ -4,7 +4,9 @@ package account
import (
"context"
stdErrors "errors"
"fmt"
"strings"
"github.com/break/junhong_cmp_fiber/internal/model"
"github.com/break/junhong_cmp_fiber/internal/model/dto"
@@ -13,6 +15,7 @@ import (
"github.com/break/junhong_cmp_fiber/pkg/constants"
"github.com/break/junhong_cmp_fiber/pkg/errors"
"github.com/break/junhong_cmp_fiber/pkg/middleware"
"github.com/jackc/pgx/v5/pgconn"
"golang.org/x/crypto/bcrypt"
"gorm.io/gorm"
)
@@ -31,12 +34,18 @@ type Service struct {
shopStore ShopStoreInterface
enterpriseStore middleware.EnterpriseStoreInterface
auditService AuditServiceInterface
wecomMembers WeComMemberFinder
}
type AuditServiceInterface interface {
LogOperation(ctx context.Context, log *model.AccountOperationLog)
}
// WeComMemberFinder 定义账号绑定时校验应用可见成员的边界。
type WeComMemberFinder interface {
GetVisible(ctx context.Context, applicationID uint, userID string) (*model.WeComMember, error)
}
// New 创建账号服务
func New(
accountStore *postgres.AccountStore,
@@ -58,6 +67,11 @@ func New(
}
}
// SetWeComMemberFinder 注入企业微信应用可见成员查询边界。
func (s *Service) SetWeComMemberFinder(finder WeComMemberFinder) {
s.wecomMembers = finder
}
// Create 创建账号
func (s *Service) Create(ctx context.Context, req *dto.CreateAccountRequest) (*model.Account, error) {
currentUserID := middleware.GetUserIDFromContext(ctx)
@@ -180,7 +194,7 @@ func (s *Service) Create(ctx context.Context, req *dto.CreateAccountRequest) (*m
}
// Get 获取账号
func (s *Service) Get(ctx context.Context, id uint) (*model.Account, error) {
func (s *Service) Get(ctx context.Context, id uint) (*dto.AccountResponse, error) {
account, err := s.accountStore.GetByID(ctx, id)
if err != nil {
if err == gorm.ErrRecordNotFound {
@@ -188,7 +202,66 @@ func (s *Service) Get(ctx context.Context, id uint) (*model.Account, error) {
}
return nil, errors.Wrap(errors.CodeInternalError, err, "获取账号失败")
}
return account, nil
accounts := []*model.Account{account}
return s.toAccountResponse(account, s.loadShopNames(ctx, accounts), s.loadEnterpriseNames(ctx, accounts)), nil
}
// BindWeCom 将系统账号绑定到管理员明确选择的企微应用可见成员。
func (s *Service) BindWeCom(ctx context.Context, accountID uint, request dto.BindAccountWeComRequest) (*dto.AccountResponse, error) {
operatorID := middleware.GetUserIDFromContext(ctx)
operatorType := middleware.GetUserTypeFromContext(ctx)
if operatorID == 0 {
return nil, errors.New(errors.CodeUnauthorized)
}
if operatorType != constants.UserTypeSuperAdmin && operatorType != constants.UserTypePlatform {
return nil, errors.New(errors.CodeForbidden)
}
if s.wecomMembers == nil || request.ApplicationID == 0 || strings.TrimSpace(request.UserID) == "" {
return nil, errors.New(errors.CodeInvalidParam)
}
account, err := s.accountStore.GetByID(ctx, accountID)
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil, errors.New(errors.CodeForbidden, "无权限操作该资源或资源不存在")
}
return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询待绑定账号失败")
}
member, err := s.wecomMembers.GetVisible(ctx, request.ApplicationID, request.UserID)
if err != nil {
return nil, err
}
beforeData := model.JSONB{
"wecom_corp_id": account.WeComCorpID, "wecom_userid": account.WeComUserID,
"wecom_name": account.WeComName,
}
if err := s.accountStore.BindWeCom(ctx, accountID, member.CorpID, member.UserID, member.Name, operatorID); err != nil {
var pgErr *pgconn.PgError
if stdErrors.As(err, &pgErr) && pgErr.Code == "23505" {
return nil, errors.New(errors.CodeConflict, "该企业微信成员已绑定其他系统账号")
}
return nil, errors.Wrap(errors.CodeDatabaseError, err, "绑定企业微信成员失败")
}
account.WeComCorpID = member.CorpID
account.WeComUserID = member.UserID
account.WeComName = member.Name
account.Updater = operatorID
if s.auditService != nil {
operatorName := ""
if operator, getErr := s.accountStore.GetByID(ctx, operatorID); getErr == nil {
operatorName = operator.Username
}
s.auditService.LogOperation(ctx, &model.AccountOperationLog{
OperatorID: operatorID, OperatorType: operatorType, OperatorName: operatorName,
TargetAccountID: &account.ID, TargetUsername: &account.Username, TargetUserType: &account.UserType,
OperationType: "bind_wecom", OperationDesc: fmt.Sprintf("绑定账号企业微信成员: %s", account.Username),
BeforeData: beforeData, AfterData: model.JSONB{
"wecom_corp_id": member.CorpID, "wecom_userid": member.UserID, "wecom_name": member.Name,
}, RequestID: middleware.GetRequestIDFromContext(ctx), IPAddress: middleware.GetIPFromContext(ctx),
UserAgent: middleware.GetUserAgentFromContext(ctx),
})
}
accounts := []*model.Account{account}
return s.toAccountResponse(account, s.loadShopNames(ctx, accounts), s.loadEnterpriseNames(ctx, accounts)), nil
}
// Update 更新账号
@@ -796,6 +869,10 @@ func (s *Service) toAccountResponse(acc *model.Account, shopMap map[uint]string,
UserType: acc.UserType,
ShopID: acc.ShopID,
EnterpriseID: acc.EnterpriseID,
WeComCorpID: acc.WeComCorpID,
WeComUserID: acc.WeComUserID,
WeComName: acc.WeComName,
WeComBound: acc.WeComCorpID != "" && acc.WeComUserID != "",
Status: acc.Status,
StatusName: constants.GetStatusName(acc.Status),
Creator: acc.Creator,