Explorar el Código

记录商户端人员操作

zc hace 3 meses
padre
commit
2785c6d0ee

+ 78 - 0
alien-entity/src/main/java/shop/alien/entity/storePlatform/StorePlatformOperationLog.java

@@ -0,0 +1,78 @@
+package shop.alien.entity.storePlatform;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * 平台操作记录表
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Data
+@TableName("store_platform_operation_log")
+@ApiModel(value = "StorePlatformOperationLog对象", description = "平台操作记录")
+public class StorePlatformOperationLog {
+
+    @ApiModelProperty(value = "主键ID")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    @ApiModelProperty(value = "操作模块")
+    @TableField("operation_module")
+    private String operationModule;
+
+    @ApiModelProperty(value = "操作类型")
+    @TableField("operation_type")
+    private String operationType;
+
+    @ApiModelProperty(value = "操作内容")
+    @TableField("operation_content")
+    private String operationContent;
+
+    @ApiModelProperty(value = "操作入参(JSON)")
+    @TableField("operation_params")
+    private String operationParams;
+
+    @ApiModelProperty(value = "操作者ID")
+    @TableField("operator_id")
+    private String operatorId;
+
+    @ApiModelProperty(value = "操作者账号")
+    @TableField("operator_account")
+    private String operatorAccount;
+
+    @ApiModelProperty(value = "操作者姓名")
+    @TableField("operator_name")
+    private String operatorName;
+
+    @ApiModelProperty(value = "用户类型")
+    @TableField("user_type")
+    private String userType;
+
+    @ApiModelProperty(value = "请求方法")
+    @TableField("request_method")
+    private String requestMethod;
+
+    @ApiModelProperty(value = "请求路径")
+    @TableField("request_path")
+    private String requestPath;
+
+    @ApiModelProperty(value = "IP地址")
+    @TableField("ip_address")
+    private String ipAddress;
+
+    @ApiModelProperty(value = "操作时间")
+    @TableField(value = "operation_time", fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date operationTime;
+}

+ 15 - 0
alien-entity/src/main/java/shop/alien/mapper/StorePlatformOperationLogMapper.java

@@ -0,0 +1,15 @@
+package shop.alien.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import shop.alien.entity.storePlatform.StorePlatformOperationLog;
+
+/**
+ * 平台操作记录 Mapper接口
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Mapper
+public interface StorePlatformOperationLogMapper extends BaseMapper<StorePlatformOperationLog> {
+}

+ 4 - 0
alien-store-platform/pom.xml

@@ -56,6 +56,10 @@
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-websocket</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-aop</artifactId>
+        </dependency>
 
         <dependency>
             <groupId>org.elasticsearch.client</groupId>

+ 31 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/annotation/PlatformOperationLog.java

@@ -0,0 +1,31 @@
+package shop.alien.storeplatform.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * 平台操作记录注解
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface PlatformOperationLog {
+
+    /**
+     * 操作模块
+     */
+    String module();
+
+    /**
+     * 操作类型
+     */
+    String type();
+
+    /**
+     * 操作内容(支持SpEL表达式)
+     * 示例:"新增角色 #{#roleName}(店铺ID=#{#storeId})"
+     */
+    String content() default "";
+}

+ 291 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/aspect/PlatformOperationLogAspect.java

@@ -0,0 +1,291 @@
+package shop.alien.storeplatform.aspect;
+
+import com.alibaba.fastjson.JSON;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.springframework.core.DefaultParameterNameDiscoverer;
+import org.springframework.core.annotation.Order;
+import org.springframework.expression.EvaluationContext;
+import org.springframework.expression.Expression;
+import org.springframework.expression.common.TemplateParserContext;
+import org.springframework.expression.spel.standard.SpelExpressionParser;
+import org.springframework.expression.spel.support.StandardEvaluationContext;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.StorePlatformMenu;
+import shop.alien.entity.store.StorePlatformRole;
+import shop.alien.entity.storePlatform.StorePlatformOperationLog;
+import shop.alien.mapper.StorePlatformMenuMapper;
+import shop.alien.storeplatform.annotation.PlatformOperationLog;
+import shop.alien.storeplatform.dto.CreateRoleDto;
+import shop.alien.storeplatform.dto.UpdateRoleDto;
+import shop.alien.storeplatform.service.PlatformOperationLogService;
+import com.alibaba.fastjson.JSONObject;
+import shop.alien.storeplatform.service.StorePlatformRoleMenuService;
+import shop.alien.storeplatform.service.StorePlatformRoleService;
+import shop.alien.storeplatform.util.LoginUserUtil;
+
+import javax.servlet.http.HttpServletRequest;
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 平台操作记录切面
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Aspect
+@Component
+@Order(10)
+@RequiredArgsConstructor
+public class PlatformOperationLogAspect {
+
+    private final PlatformOperationLogService platformOperationLogService;
+    private final StorePlatformRoleService storePlatformRoleService;
+    private final StorePlatformRoleMenuService storePlatformRoleMenuService;
+    private final StorePlatformMenuMapper storePlatformMenuMapper;
+    private final SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
+    private final DefaultParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
+    private final TemplateParserContext templateParserContext = new TemplateParserContext("#{", "}");
+
+    @Pointcut("@annotation(shop.alien.storeplatform.annotation.PlatformOperationLog)")
+    public void operationLogPointcut() {
+    }
+
+    @Around("operationLogPointcut()")
+    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
+        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
+        Method method = signature.getMethod();
+        PlatformOperationLog annotation = method.getAnnotation(PlatformOperationLog.class);
+
+        Object result;
+        try {
+            result = joinPoint.proceed();
+        } catch (Exception e) {
+            throw e;
+        }
+
+        if (annotation != null && shouldRecord(result)) {
+            try {
+                StorePlatformOperationLog logRecord = buildLogRecord(joinPoint, annotation, result);
+                platformOperationLogService.save(logRecord);
+            } catch (Exception e) {
+                log.warn("平台操作记录失败: {}", e.getMessage());
+            }
+        }
+
+        return result;
+    }
+
+    private boolean shouldRecord(Object result) {
+        if (result instanceof R) {
+            return R.isSuccess((R<?>) result);
+        }
+        return true;
+    }
+
+    private StorePlatformOperationLog buildLogRecord(ProceedingJoinPoint joinPoint, PlatformOperationLog annotation, Object result) {
+        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
+        Method method = signature.getMethod();
+        Object[] args = joinPoint.getArgs();
+        String[] parameterNames = parameterNameDiscoverer.getParameterNames(method);
+
+        EvaluationContext context = new StandardEvaluationContext();
+        if (args != null) {
+            for (int i = 0; i < args.length; i++) {
+                context.setVariable("p" + i, args[i]);
+                context.setVariable("arg" + i, args[i]);
+                if (parameterNames != null && parameterNames.length > i) {
+                    context.setVariable(parameterNames[i], args[i]);
+                }
+            }
+        }
+        context.setVariable("result", result);
+
+        StorePlatformOperationLog logRecord = new StorePlatformOperationLog();
+        logRecord.setOperationModule(annotation.module());
+        logRecord.setOperationType(annotation.type());
+        logRecord.setOperationTime(new Date());
+
+        String content = resolveContent(annotation, context, args);
+        logRecord.setOperationContent(content);
+        logRecord.setOperationParams(buildParamsJson(parameterNames, args));
+
+        fillOperatorInfo(logRecord);
+        fillRequestInfo(logRecord);
+        return logRecord;
+    }
+
+    private void fillOperatorInfo(StorePlatformOperationLog logRecord) {
+        try {
+            JSONObject userInfo = LoginUserUtil.getCurrentUserInfo();
+            if (userInfo != null) {
+                logRecord.setOperatorId(userInfo.getString("userId"));
+                String account = userInfo.getString("phone");
+                if (!StringUtils.hasText(account)) {
+                    account = userInfo.getString("account");
+                }
+                logRecord.setOperatorAccount(account);
+                String name = userInfo.getString("userName");
+                if (!StringUtils.hasText(name)) {
+                    name = userInfo.getString("name");
+                }
+                logRecord.setOperatorName(name);
+                String userType = userInfo.getString("userType");
+                logRecord.setUserType(StringUtils.hasText(userType) ? userType : "platform");
+            }
+        } catch (Exception e) {
+            log.debug("填充操作人信息失败", e);
+        }
+    }
+
+    private void fillRequestInfo(StorePlatformOperationLog logRecord) {
+        try {
+            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+            if (attributes == null) {
+                return;
+            }
+            HttpServletRequest request = attributes.getRequest();
+            logRecord.setRequestMethod(request.getMethod());
+            logRecord.setRequestPath(request.getRequestURI());
+            logRecord.setIpAddress(getIpAddress(request));
+        } catch (Exception e) {
+            log.debug("填充请求信息失败", e);
+        }
+    }
+
+    private String getIpAddress(HttpServletRequest request) {
+        String ip = request.getHeader("X-Forwarded-For");
+        if (!StringUtils.hasText(ip) || "unknown".equalsIgnoreCase(ip)) {
+            ip = request.getHeader("Proxy-Client-IP");
+        }
+        if (!StringUtils.hasText(ip) || "unknown".equalsIgnoreCase(ip)) {
+            ip = request.getHeader("WL-Proxy-Client-IP");
+        }
+        if (!StringUtils.hasText(ip) || "unknown".equalsIgnoreCase(ip)) {
+            ip = request.getHeader("HTTP_CLIENT_IP");
+        }
+        if (!StringUtils.hasText(ip) || "unknown".equalsIgnoreCase(ip)) {
+            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
+        }
+        if (!StringUtils.hasText(ip) || "unknown".equalsIgnoreCase(ip)) {
+            ip = request.getRemoteAddr();
+        }
+        return ip;
+    }
+
+    private String resolveContent(PlatformOperationLog annotation, EvaluationContext context, Object[] args) {
+        if ("角色操作记录".equals(annotation.module())) {
+            return buildRoleOperationContent(annotation.type(), args);
+        }
+        String content = annotation.content();
+        if (StringUtils.hasText(content)) {
+            Expression exp = spelExpressionParser.parseExpression(content, templateParserContext);
+            String parsed = exp.getValue(context, String.class);
+            return parsed != null ? parsed : annotation.type();
+        }
+        return annotation.type();
+    }
+
+    private String buildParamsJson(String[] parameterNames, Object[] args) {
+        try {
+            if (args == null || args.length == 0) {
+                return null;
+            }
+            if (parameterNames == null || parameterNames.length == 0) {
+                return JSON.toJSONString(args);
+            }
+            java.util.Map<String, Object> params = new java.util.LinkedHashMap<>();
+            for (int i = 0; i < args.length; i++) {
+                String name = parameterNames.length > i ? parameterNames[i] : "arg" + i;
+                params.put(name, args[i]);
+            }
+            return JSON.toJSONString(params);
+        } catch (Exception e) {
+            log.debug("序列化操作入参失败", e);
+            return null;
+        }
+    }
+
+    private String buildRoleOperationContent(String operationType, Object[] args) {
+        String roleName = null;
+        List<Long> menuIds = Collections.emptyList();
+        Long roleId = null;
+
+        if (args != null && args.length > 0) {
+            Object firstArg = args[0];
+            if (firstArg instanceof StorePlatformRole) {
+                StorePlatformRole role = (StorePlatformRole) firstArg;
+                roleName = role.getRoleName();
+            } else if (firstArg instanceof CreateRoleDto) {
+                CreateRoleDto dto = (CreateRoleDto) firstArg;
+                roleName = dto.getRoleName();
+                menuIds = dto.getMenuIds() != null ? dto.getMenuIds() : Collections.emptyList();
+            } else if (firstArg instanceof UpdateRoleDto) {
+                UpdateRoleDto dto = (UpdateRoleDto) firstArg;
+                roleName = dto.getRoleName();
+                roleId = dto.getRoleId();
+                menuIds = dto.getMenuIds() != null ? dto.getMenuIds() : Collections.emptyList();
+            } else if (firstArg instanceof Long) {
+                roleId = (Long) firstArg;
+            }
+        }
+
+        if ((roleName == null || roleName.trim().isEmpty()) && roleId != null) {
+            StorePlatformRole role = storePlatformRoleService.getRoleById(roleId);
+            if (role != null) {
+                roleName = role.getRoleName();
+            }
+        }
+
+        if ((menuIds == null || menuIds.isEmpty()) && roleId != null) {
+            menuIds = storePlatformRoleMenuService.getMenuIdsByRoleId(roleId);
+        }
+
+        List<String> menuNames = resolveMenuNames(menuIds);
+        int count = menuNames.size();
+        String menuSummary = menuNames.isEmpty() ? "无权限" : menuNames.get(0);
+        String roleLabel = StringUtils.hasText(roleName) ? roleName : "未知";
+        String type = normalizeRoleOperationType(operationType);
+        return String.format("%s%s角色标签,包含%s等%d个权限", type, roleLabel, menuSummary, count);
+    }
+
+    private List<String> resolveMenuNames(List<Long> menuIds) {
+        if (menuIds == null || menuIds.isEmpty()) {
+            return Collections.emptyList();
+        }
+        return storePlatformMenuMapper.selectBatchIds(menuIds).stream()
+                .map(StorePlatformMenu::getMenuName)
+                .filter(StringUtils::hasText)
+                .collect(Collectors.toList());
+    }
+
+    private String normalizeRoleOperationType(String operationType) {
+        if (operationType == null) {
+            return "操作";
+        }
+        if (operationType.contains("新增")) {
+            return "新增";
+        }
+        if (operationType.contains("修改")) {
+            return "修改";
+        }
+        if (operationType.contains("删除")) {
+            return "删除";
+        }
+        return operationType;
+    }
+}

+ 71 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/controller/PlatformOperationLogController.java

@@ -0,0 +1,71 @@
+package shop.alien.storeplatform.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiOperationSupport;
+import io.swagger.annotations.ApiSort;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.format.annotation.DateTimeFormat;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+import shop.alien.entity.result.R;
+import shop.alien.entity.storePlatform.StorePlatformOperationLog;
+import shop.alien.storeplatform.enums.PlatformOperationModule;
+import shop.alien.storeplatform.service.PlatformOperationLogService;
+
+import java.util.Date;
+
+/**
+ * 平台操作记录查询接口
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Api(tags = {"平台-操作记录"})
+@ApiSort(99)
+@RestController
+@RequestMapping("/platform/operationLog")
+@RequiredArgsConstructor
+public class PlatformOperationLogController {
+
+    private final PlatformOperationLogService platformOperationLogService;
+
+    @ApiOperation("分页查询操作记录")
+    @ApiOperationSupport(order = 1)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "page", value = "页码", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "size", value = "页容", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "module", value = "操作模块(ACCOUNT/ROLE)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "account", value = "操作账号(模糊查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "startTime", value = "开始时间", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "endTime", value = "结束时间", dataType = "String", paramType = "query")
+    })
+    @GetMapping("/page")
+    public R<IPage<StorePlatformOperationLog>> getPage(
+            @RequestParam(name = "page", defaultValue = "1") int page,
+            @RequestParam(name = "size", defaultValue = "10") int size,
+            @RequestParam(name = "module", required = false) String module,
+            @RequestParam(name = "account", required = false) String account,
+            @RequestParam(name = "startTime", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date startTime,
+            @RequestParam(name = "endTime", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date endTime) {
+        String moduleName = resolveModuleName(module);
+        log.info("PlatformOperationLogController.getPage?page={}, size={}, module={}, account={}, startTime={}, endTime={}",
+                page, size, moduleName, account, startTime, endTime);
+        return R.data(platformOperationLogService.getOperationLogPage(page, size, moduleName, account, startTime, endTime));
+    }
+
+    private String resolveModuleName(String module) {
+        if (!org.springframework.util.StringUtils.hasText(module)) {
+            return null;
+        }
+        PlatformOperationModule operationModule = PlatformOperationModule.fromCode(module);
+        return operationModule != null ? operationModule.getModuleName() : null;
+    }
+}

+ 21 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/controller/StorePlatformRoleController.java

@@ -9,6 +9,7 @@ import shop.alien.entity.result.R;
 import shop.alien.entity.store.StorePlatformRole;
 import shop.alien.entity.store.vo.RolePermissionTableVo;
 import shop.alien.entity.store.vo.StorePlatformRoleVo;
+import shop.alien.storeplatform.annotation.PlatformOperationLog;
 import shop.alien.storeplatform.dto.CreateRoleDto;
 import shop.alien.storeplatform.dto.UpdateRoleDto;
 import shop.alien.storeplatform.service.StorePlatformRoleMenuService;
@@ -75,6 +76,11 @@ public class StorePlatformRoleController {
 
     @ApiOperation("新增角色")
     @ApiOperationSupport(order = 3)
+    @PlatformOperationLog(
+            module = "角色操作记录",
+            type = "新增角色",
+            content = "新增角色 #{#p0.roleName}(店铺ID=#{#p0.storeId})"
+    )
     @PostMapping("/saveRole")
     public R<String> saveRole(@RequestBody StorePlatformRole role) {
         log.info("StorePlatformRoleController.saveRole?role={}", role);
@@ -91,6 +97,11 @@ public class StorePlatformRoleController {
 
     @ApiOperation("创建角色(包含权限分配)")
     @ApiOperationSupport(order = 3)
+    @PlatformOperationLog(
+            module = "角色操作记录",
+            type = "新增角色",
+            content = "新增角色 #{#p0.roleName}(店铺ID=#{#p0.storeId},权限数=#{#p0.menuIds != null ? #p0.menuIds.size() : 0})"
+    )
     @PostMapping("/createRole")
     public R<String> createRole(@RequestBody CreateRoleDto createRoleDto) {
         log.info("StorePlatformRoleController.createRole?storeId={}, roleName={}, menuIds={}", 
@@ -121,6 +132,11 @@ public class StorePlatformRoleController {
 
     @ApiOperation("修改角色")
     @ApiOperationSupport(order = 4)
+    @PlatformOperationLog(
+            module = "角色操作记录",
+            type = "修改角色",
+            content = "修改角色ID=#{#p0.roleId},名称=#{#p0.roleName}(店铺ID=#{#p0.storeId})"
+    )
     @PostMapping("/updateRole")
     public R<String> updateRole(@RequestBody UpdateRoleDto updateRoleDto) {
         log.info("StorePlatformRoleController.updateRole?roleId={}, roleName={}, menuIds={}", 
@@ -158,6 +174,11 @@ public class StorePlatformRoleController {
     @ApiImplicitParams({
             @ApiImplicitParam(name = "roleId", value = "角色ID", dataType = "Long", paramType = "query", required = true)
     })
+    @PlatformOperationLog(
+            module = "角色操作记录",
+            type = "删除角色",
+            content = "删除角色ID=#{#roleId}"
+    )
     @DeleteMapping("/deleteRole")
     public R<String> deleteRole(@RequestParam("roleId") Long roleId) {
         log.info("StorePlatformRoleController.deleteRole?roleId={}", roleId);

+ 51 - 22
alien-store-platform/src/main/java/shop/alien/storeplatform/controller/StorePlatformUserRoleController.java

@@ -1,14 +1,14 @@
 package shop.alien.storeplatform.controller;
 
 import io.swagger.annotations.*;
+import lombok.Data;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.web.bind.annotation.*;
 import shop.alien.entity.result.R;
-import shop.alien.entity.store.vo.SubAccountVo;
-import shop.alien.storeplatform.dto.AssignRolesDto;
-import shop.alien.storeplatform.dto.CreateAccountDto;
 import shop.alien.storeplatform.service.StorePlatformUserRoleService;
+import shop.alien.storeplatform.annotation.PlatformOperationLog;
+
 
 import java.util.List;
 
@@ -74,6 +74,11 @@ public class StorePlatformUserRoleController {
             @ApiImplicitParam(name = "userId", value = "用户ID", dataType = "int", paramType = "query", required = true),
             @ApiImplicitParam(name = "storeId", value = "店铺ID", dataType = "int", paramType = "query", required = true)
     })
+    @PlatformOperationLog(
+            module = "账号操作记录",
+            type = "移除角色",
+            content = "移除用户ID=#{#userId}的全部角色(店铺ID=#{#storeId})"
+    )
     @DeleteMapping("/removeAllRoles")
     public R<String> removeAllRoles(@RequestParam("userId") Integer userId, @RequestParam("storeId") Integer storeId) {
         log.info("StorePlatformUserRoleController.removeAllRoles?userId={}, storeId={}", userId, storeId);
@@ -91,6 +96,11 @@ public class StorePlatformUserRoleController {
             @ApiImplicitParam(name = "roleId", value = "角色ID", dataType = "Long", paramType = "query", required = true),
             @ApiImplicitParam(name = "storeId", value = "店铺ID", dataType = "int", paramType = "query", required = true)
     })
+    @PlatformOperationLog(
+            module = "账号操作记录",
+            type = "移除角色",
+            content = "移除用户ID=#{#userId}的角色ID=#{#roleId}(店铺ID=#{#storeId})"
+    )
     @DeleteMapping("/removeRole")
     public R<String> removeRole(@RequestParam("userId") Integer userId, @RequestParam("roleId") Long roleId, @RequestParam("storeId") Integer storeId) {
         log.info("StorePlatformUserRoleController.removeRole?userId={}, roleId={}, storeId={}", userId, roleId, storeId);
@@ -103,6 +113,11 @@ public class StorePlatformUserRoleController {
 
     @ApiOperation("创建账号并分配角色")
     @ApiOperationSupport(order = 6)
+    @PlatformOperationLog(
+            module = "账号操作记录",
+            type = "新增子账号",
+            content = "新增账号手机号:#{#p0.phone},账号名:#{#p0.accountName}"
+    )
     @PostMapping("/createAccountAndAssignRole")
     public R<String> createAccountAndAssignRole(@RequestBody CreateAccountDto createAccountDto) {
         log.info("StorePlatformUserRoleController.createAccountAndAssignRole?phone={}, accountName={}, storeId={}, roleId={}", 
@@ -118,25 +133,39 @@ public class StorePlatformUserRoleController {
         return R.fail("创建账号并分配角色失败");
     }
 
-    @ApiOperation("查询当前店铺下的子账号列表")
-    @ApiOperationSupport(order = 7)
-    @ApiImplicitParams({
-            @ApiImplicitParam(name = "storeId", value = "店铺ID", dataType = "int", paramType = "query", required = true),
-            @ApiImplicitParam(name = "accountName", value = "账号名称(模糊查询)", dataType = "String", paramType = "query", required = false),
-            @ApiImplicitParam(name = "phone", value = "手机号(模糊查询)", dataType = "String", paramType = "query", required = false),
-            @ApiImplicitParam(name = "roleName", value = "角色名称(模糊查询)", dataType = "String", paramType = "query", required = false)
-    })
-    @GetMapping("/querySubAccounts")
-    public R<List<SubAccountVo>> querySubAccounts(
-            @RequestParam("storeId") Integer storeId,
-            @RequestParam(value = "accountName", required = false) String accountName,
-            @RequestParam(value = "phone", required = false) String phone,
-            @RequestParam(value = "roleName", required = false) String roleName) {
-        log.info("StorePlatformUserRoleController.querySubAccounts?storeId={}, accountName={}, phone={}, roleName={}", 
-                storeId, accountName, phone, roleName);
-        List<SubAccountVo> subAccountList = storePlatformUserRoleService.querySubAccounts(
-                storeId, accountName, phone, roleName);
-        return R.data(subAccountList);
+    /**
+     * 分配角色请求DTO
+     */
+    @Data
+    @ApiModel(value = "AssignRolesDto", description = "分配角色请求参数")
+    static class AssignRolesDto {
+        @ApiModelProperty(value = "用户ID", required = true)
+        private Integer userId;
+
+        @ApiModelProperty(value = "角色ID列表", required = true)
+        private List<Long> roleIds;
+
+        @ApiModelProperty(value = "店铺ID", required = true)
+        private Integer storeId;
+    }
+
+    /**
+     * 创建账号请求DTO
+     */
+    @Data
+    @ApiModel(value = "CreateAccountDto", description = "创建账号请求参数")
+    static class CreateAccountDto {
+        @ApiModelProperty(value = "手机号", required = true)
+        private String phone;
+
+        @ApiModelProperty(value = "账号名称")
+        private String accountName;
+
+        @ApiModelProperty(value = "店铺ID", required = true)
+        private Integer storeId;
+
+        @ApiModelProperty(value = "角色ID", required = true)
+        private Long roleId;
     }
 }
 

+ 40 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/enums/PlatformOperationModule.java

@@ -0,0 +1,40 @@
+package shop.alien.storeplatform.enums;
+
+/**
+ * 平台操作记录模块枚举
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+public enum PlatformOperationModule {
+    ACCOUNT("ACCOUNT", "账号操作记录"),
+    ROLE("ROLE", "角色操作记录");
+
+    private final String code;
+    private final String moduleName;
+
+    PlatformOperationModule(String code, String moduleName) {
+        this.code = code;
+        this.moduleName = moduleName;
+    }
+
+    public String getCode() {
+        return code;
+    }
+
+    public String getModuleName() {
+        return moduleName;
+    }
+
+    public static PlatformOperationModule fromCode(String code) {
+        if (code == null) {
+            return null;
+        }
+        for (PlatformOperationModule module : values()) {
+            if (module.code.equalsIgnoreCase(code)) {
+                return module;
+            }
+        }
+        return null;
+    }
+}

+ 35 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/service/PlatformOperationLogService.java

@@ -0,0 +1,35 @@
+package shop.alien.storeplatform.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import shop.alien.entity.storePlatform.StorePlatformOperationLog;
+
+import java.util.Date;
+
+/**
+ * 平台操作记录服务接口
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+public interface PlatformOperationLogService {
+
+    /**
+     * 保存操作记录
+     *
+     * @param logRecord 操作记录
+     */
+    void save(StorePlatformOperationLog logRecord);
+
+    /**
+     * 分页查询操作记录
+     *
+     * @param page      页码
+     * @param size      页容
+     * @param module    操作模块
+     * @param account   操作账号(模糊查询)
+     * @param startTime 开始时间
+     * @param endTime   结束时间
+     * @return IPage<StorePlatformOperationLog>
+     */
+    IPage<StorePlatformOperationLog> getOperationLogPage(int page, int size, String module, String account, Date startTime, Date endTime);
+}

+ 57 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/PlatformOperationLogServiceImpl.java

@@ -0,0 +1,57 @@
+package shop.alien.storeplatform.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+import shop.alien.entity.storePlatform.StorePlatformOperationLog;
+import shop.alien.mapper.StorePlatformOperationLogMapper;
+import shop.alien.storeplatform.service.PlatformOperationLogService;
+
+import java.util.Date;
+
+/**
+ * 平台操作记录服务实现类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class PlatformOperationLogServiceImpl implements PlatformOperationLogService {
+
+    private final StorePlatformOperationLogMapper storePlatformOperationLogMapper;
+
+    @Override
+    public void save(StorePlatformOperationLog logRecord) {
+        try {
+            storePlatformOperationLogMapper.insert(logRecord);
+        } catch (Exception e) {
+            log.warn("保存平台操作记录失败: {}", e.getMessage(), e);
+        }
+    }
+
+    @Override
+    public IPage<StorePlatformOperationLog> getOperationLogPage(int page, int size, String module, String account, Date startTime, Date endTime) {
+        Page<StorePlatformOperationLog> pageInfo = new Page<>(page, size);
+        LambdaQueryWrapper<StorePlatformOperationLog> wrapper = new LambdaQueryWrapper<>();
+        if (StringUtils.hasText(module)) {
+            wrapper.eq(StorePlatformOperationLog::getOperationModule, module);
+        }
+        if (StringUtils.hasText(account)) {
+            wrapper.like(StorePlatformOperationLog::getOperatorAccount, account);
+        }
+        if (startTime != null) {
+            wrapper.ge(StorePlatformOperationLog::getOperationTime, startTime);
+        }
+        if (endTime != null) {
+            wrapper.le(StorePlatformOperationLog::getOperationTime, endTime);
+        }
+        wrapper.orderByDesc(StorePlatformOperationLog::getOperationTime);
+        return storePlatformOperationLogMapper.selectPage(pageInfo, wrapper);
+    }
+}