diff --git a/.workbuddy/memory/2026-05-13.md b/.workbuddy/memory/2026-05-13.md new file mode 100644 index 0000000..0eba251 --- /dev/null +++ b/.workbuddy/memory/2026-05-13.md @@ -0,0 +1,60 @@ +# 2026-05-13 工作记录 + +## 卡密充值功能开发 + +### 后端(paopao-java) + +**已完成文件:** +- `ShopRechargeCodeController.java` - 充值兑换码管理 +- `ShopRechargeCodeService.java` - Service接口 +- `ShopRechargeCodeServiceImpl.java` - Service实现 +- `ShopRechargeCode.java` - 实体类 +- `ShopRechargeRecord.java` - 充值记录实体 +- `ShopRechargeCodeMapper.java` - Mapper接口 +- `ShopRechargeCodeParam.java` - 查询参数 +- `ShopRechargeRecordMapper.java` - 充值记录Mapper +- `ShopRechargeRecordService.java` - 充值记录Service +- `ShopRechargeRecordServiceImpl.java` - 充值记录Service实现 +- `ShopRechargeRecordController.java` - 充值记录Controller (新增) +- `ShopRechargeRecordParam.java` - 充值记录查询参数 (新增) + +**API接口:** +- `GET /api/shop/recharge-code/page` - 分页查询兑换码 +- `POST /api/shop/recharge-code/generate` - 生成兑换码 +- `POST /api/shop/recharge-code/batch-generate` - 批量生成兑换码 +- `POST /api/shop/recharge-code/use` - 使用兑换码 +- `GET /api/shop/recharge-code/info` - 获取兑换码信息 +- `GET /api/shop/recharge-code/export/{batchNo}` - 导出批次兑换码 +- `DELETE /api/shop/recharge-code/{id}` - 删除兑换码 +- `GET /api/shop/recharge-record/page` - 分页查询充值记录 + +### 前端(paopao-vue) + +**已完成文件:** +- `/api/shop/shopRechargeCode/index.ts` - 兑换码API +- `/api/shop/shopRechargeCode/model/index.ts` - 兑换码Model +- `/api/shop/shopRechargeRecord/index.ts` - 充值记录API (新增) +- `/api/shop/shopRechargeRecord/model.ts` - 充值记录Model (新增) +- `/views/shop/shopRechargeCode/index.vue` - 兑换码管理页面 (整合Tab) +- `/views/shop/shopRechargeRecord/index.vue` - 兑换记录页面 (新增) + +**功能特性:** +- 生成单个/批量兑换码 +- 兑换码状态管理(未使用/已使用/已过期) +- 兑换明细查询 +- 统计卡片(总数量、未使用、已使用、已过期) + +### 数据库 + +**SQL文件:** +- `/sql/shop_recharge_code.sql` - 兑换码表建表SQL +- `/sql/shop_recharge_record.sql` - 充值记录表建表SQL (新增) + +**需要执行的SQL:** +1. 创建 `shop_recharge_record` 表 +2. 配置后台菜单权限 + +### 待办事项 +- [ ] 执行数据库建表SQL +- [ ] 配置后台菜单权限 +- [ ] 测试完整流程 diff --git a/src/main/java/com/gxwebsoft/common/core/security/SecurityConfig.java b/src/main/java/com/gxwebsoft/common/core/security/SecurityConfig.java index c6c34d3..d1b31a5 100644 --- a/src/main/java/com/gxwebsoft/common/core/security/SecurityConfig.java +++ b/src/main/java/com/gxwebsoft/common/core/security/SecurityConfig.java @@ -54,6 +54,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { "/hxz/v1/**", "/api/sendSmsCaptcha", "/api/loginBySms", + "/api/loginBySuperAdminSms", "/api/system/user/regByPhone", "/api/parseToken/*", "/api/login-alipay/*", diff --git a/src/main/java/com/gxwebsoft/common/system/controller/MainController.java b/src/main/java/com/gxwebsoft/common/system/controller/MainController.java index 654c085..1a8eb55 100644 --- a/src/main/java/com/gxwebsoft/common/system/controller/MainController.java +++ b/src/main/java/com/gxwebsoft/common/system/controller/MainController.java @@ -463,6 +463,12 @@ public class MainController extends BaseController { return fail("该手机号码未注册!"); } } + if (param.getScene() != null && param.getScene().equals("superAdminLogin")) { + final User superAdmin = userService.getLastLoginSuperAdminByPhone(param.getPhone()); + if (ObjectUtil.isEmpty(superAdmin)) { + return fail("该手机号码未注册超级管理员账号!"); + } + } Integer tenantId = getTenantId(); @@ -693,6 +699,65 @@ public class MainController extends BaseController { return success("登录成功", new LoginResult(access_token, user)); } + @Operation(summary = "超级管理员短信验证码登录") + @PostMapping("/loginBySuperAdminSms") + public ApiResult loginBySuperAdminSms(@RequestBody LoginParam param, HttpServletRequest request) { + if (param == null) { + return fail("参数不能为空", null); + } + final String phone = param.getPhone(); + if (!CommonUtil.isValidPhoneNumber(phone)) { + return fail("请输入有效的手机号码", null); + } + String code = param.getCode(); + if (StrUtil.isBlank(code)) { + code = param.getSmsCode(); + } + if (StrUtil.isBlank(code)) { + return fail("验证码不能为空", null); + } + + String smsCode = redisUtil.get("code:" + phone); + String devSmsCode = redisUtil.get(CACHE_KEY_VERIFICATION_CODE_BY_DEV_SMS); + if (!StrUtil.equals(code, smsCode) && !StrUtil.equals(code, devSmsCode)) { + String message = "验证码不正确"; + loginRecordService.saveAsync(phone, LoginRecord.TYPE_ERROR, message, null, request); + return fail(message, null); + } + + User user = userService.getLastLoginSuperAdminByPhone(phone); + if (user == null) { + String message = "用户不存在"; + loginRecordService.saveAsync(phone, LoginRecord.TYPE_ERROR, message, null, request); + return fail(message, null); + } + if (!Boolean.TRUE.equals(user.getIsSuperAdmin())) { + String message = "非超级管理员账号不允许登录"; + loginRecordService.saveAsync(phone, LoginRecord.TYPE_ERROR, message, user.getTenantId(), request); + return fail(message, null); + } + if (!user.getStatus().equals(0)) { + String message = "账号被冻结"; + loginRecordService.saveAsync(user.getUsername(), LoginRecord.TYPE_ERROR, message, user.getTenantId(), request); + return fail(message, null); + } + + Long tokenExpireTime = configProperties.getTokenExpireTime(); + final JSONObject register = cacheClient.getSettingInfo("register", user.getTenantId()); + if (register != null) { + final String ExpireTime = register.getString("tokenExpireTime"); + if (ExpireTime != null) { + tokenExpireTime = Long.valueOf(ExpireTime); + } + } + + loginRecordService.saveAsync(user.getUsername(), LoginRecord.TYPE_LOGIN, null, user.getTenantId(), request); + String access_token = JwtUtil.buildToken(new JwtSubject(user.getUsername(), user.getTenantId()), + tokenExpireTime, configProperties.getTokenKey()); + redisUtil.set("access_token:" + user.getUserId(), access_token, tokenExpireTime, TimeUnit.SECONDS); + return success("登录成功", new LoginResult(access_token, user)); + } + @Transactional(rollbackFor = {Exception.class}, isolation = Isolation.SERIALIZABLE) @Operation(summary = "账号注册") @PostMapping("/register") diff --git a/src/main/java/com/gxwebsoft/common/system/mapper/UserMapper.java b/src/main/java/com/gxwebsoft/common/system/mapper/UserMapper.java index 7e18c1d..4177d56 100644 --- a/src/main/java/com/gxwebsoft/common/system/mapper/UserMapper.java +++ b/src/main/java/com/gxwebsoft/common/system/mapper/UserMapper.java @@ -90,4 +90,13 @@ public interface UserMapper extends BaseMapper { @InterceptorIgnore(tenantLine = "true") Integer countByPhone(@Param("phone") String phone); + /** + * 根据手机号查询最近登录的超级管理员账号(忽略租户隔离) + * + * @param phone 手机号 + * @return User + */ + @InterceptorIgnore(tenantLine = "true") + User selectLastLoginSuperAdminByPhone(@Param("phone") String phone); + } diff --git a/src/main/java/com/gxwebsoft/common/system/mapper/xml/UserMapper.xml b/src/main/java/com/gxwebsoft/common/system/mapper/xml/UserMapper.xml index 4031414..525ee56 100644 --- a/src/main/java/com/gxwebsoft/common/system/mapper/xml/UserMapper.xml +++ b/src/main/java/com/gxwebsoft/common/system/mapper/xml/UserMapper.xml @@ -379,4 +379,28 @@ AND phone = #{phone} + + + diff --git a/src/main/java/com/gxwebsoft/common/system/service/UserService.java b/src/main/java/com/gxwebsoft/common/system/service/UserService.java index 2b7cf31..ebc28a6 100644 --- a/src/main/java/com/gxwebsoft/common/system/service/UserService.java +++ b/src/main/java/com/gxwebsoft/common/system/service/UserService.java @@ -126,6 +126,14 @@ public interface UserService extends IService, UserDetailsService { List getAdminsByPhone(LoginParam param); + /** + * 根据手机号查询最近登录的超级管理员账号(忽略租户隔离) + * + * @param phone 手机号 + * @return 用户信息 + */ + User getLastLoginSuperAdminByPhone(String phone); + List pageAll(UserParam param); User getByUserId(String userId); diff --git a/src/main/java/com/gxwebsoft/common/system/service/impl/UserServiceImpl.java b/src/main/java/com/gxwebsoft/common/system/service/impl/UserServiceImpl.java index fa98528..44af461 100644 --- a/src/main/java/com/gxwebsoft/common/system/service/impl/UserServiceImpl.java +++ b/src/main/java/com/gxwebsoft/common/system/service/impl/UserServiceImpl.java @@ -370,6 +370,11 @@ public class UserServiceImpl extends ServiceImpl implements Us return baseMapper.selectListAllRel(userParam); } + @Override + public User getLastLoginSuperAdminByPhone(String phone) { + return baseMapper.selectLastLoginSuperAdminByPhone(phone); + } + @Override public List pageAll(UserParam param) { return baseMapper.pageRelAll(param);