Commit 5889fc8facde4774e54ddbf34df14b812cbd2436
Committed by
JEECG开源社区
1 parent
1183dd0a
!62 新增监控在线用户
* 去掉durid广告 * 新增监控在线用户功能 * 新增监控在线用户功能
Showing
7 changed files
with
443 additions
and
0 deletions
ant-design-vue-jeecg/src/api/login.js
... | ... | @@ -71,4 +71,17 @@ export function thirdLogin(token,thirdType) { |
71 | 71 | 'Content-Type': 'application/json;charset=UTF-8' |
72 | 72 | } |
73 | 73 | }) |
74 | +} | |
75 | + | |
76 | +/** | |
77 | + * 强退其他账号 | |
78 | + * @param token | |
79 | + * @returns {*} | |
80 | + */ | |
81 | +export function forceLogout(parameter) { | |
82 | + return axios({ | |
83 | + url: '/sys/online/forceLogout', | |
84 | + method: 'post', | |
85 | + data: parameter | |
86 | + }) | |
74 | 87 | } |
75 | 88 | \ No newline at end of file |
... | ... |
ant-design-vue-jeecg/src/views/system/SysOnlineList.vue
0 → 100644
1 | +<template> | |
2 | + <a-card :bordered="false"> | |
3 | + <!-- 查询区域 --> | |
4 | + <div class="table-page-search-wrapper"> | |
5 | + <a-form layout="inline" @keyup.enter.native="searchQuery"> | |
6 | + <a-row :gutter="24"> | |
7 | + <a-col :md="6" :sm="12"> | |
8 | + <a-form-item label="账号"> | |
9 | + <a-input placeholder="请输入账号查询" v-model="queryParam.username"></a-input> | |
10 | + </a-form-item> | |
11 | + </a-col> | |
12 | + <a-col :md="6" :sm="8"> | |
13 | + <span style="float: left;overflow: hidden;" class="table-page-search-submitButtons"> | |
14 | + <a-button type="primary" @click="searchQuery" icon="search">查询</a-button> | |
15 | + <a-button type="primary" @click="searchReset" icon="reload" style="margin-left: 8px">重置</a-button> | |
16 | + </span> | |
17 | + </a-col> | |
18 | + </a-row> | |
19 | + </a-form> | |
20 | + </div> | |
21 | + <!-- 查询区域-END --> | |
22 | + | |
23 | + <!-- table区域-begin --> | |
24 | + <div> | |
25 | + <div class="ant-alert ant-alert-info" style="margin-bottom: 16px;"> | |
26 | + <i class="anticon anticon-info-circle ant-alert-icon"></i> 已选择 <a style="font-weight: 600">{{ selectedRowKeys.length }}</a>项 | |
27 | + <a style="margin-left: 24px" @click="onClearSelected">清空</a> | |
28 | + </div> | |
29 | + | |
30 | + <a-table | |
31 | + ref="table" | |
32 | + size="middle" | |
33 | + :scroll="{x:true}" | |
34 | + bordered | |
35 | + rowKey="token" | |
36 | + :columns="columns" | |
37 | + :dataSource="dataSource" | |
38 | + :pagination="ipagination" | |
39 | + :loading="loading" | |
40 | + :rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}" | |
41 | + class="j-table-force-nowrap" | |
42 | + @change="handleTableChange"> | |
43 | + | |
44 | + <template slot="avatarslot" slot-scope="text, record, index"> | |
45 | + <div class="anty-img-wrap"> | |
46 | + <a-avatar shape="square" :src="getAvatarView(record.avatar)" icon="user"/> | |
47 | + </div> | |
48 | + </template> | |
49 | + | |
50 | + <span slot="action" slot-scope="text, record"> | |
51 | + <a-popconfirm title="强制退出用户?" @confirm="() => handleForce(record)"> | |
52 | + <a-button type="danger">强退</a-button> | |
53 | + </a-popconfirm> | |
54 | + </span> | |
55 | + | |
56 | + </a-table> | |
57 | + </div> | |
58 | + | |
59 | + </a-card> | |
60 | +</template> | |
61 | + | |
62 | +<script> | |
63 | + | |
64 | + import '@/assets/less/TableExpand.less' | |
65 | + import { mixinDevice } from '@/utils/mixin' | |
66 | + import { JeecgListMixin } from '@/mixins/JeecgListMixin' | |
67 | + import { forceLogout } from '@/api/login' | |
68 | + import {filterDictTextByCache} from '@/components/dict/JDictSelectUtil' | |
69 | + | |
70 | + import {getFileAccessHttpUrl} from '@/api/manage'; | |
71 | + | |
72 | + export default { | |
73 | + name: "OnlineList", | |
74 | + mixins:[JeecgListMixin, mixinDevice], | |
75 | + components: {}, | |
76 | + data () { | |
77 | + return { | |
78 | + description: '在线用户管理页面', | |
79 | + queryParam: { | |
80 | + username: '' | |
81 | + }, | |
82 | + // 表头 | |
83 | + columns: [ | |
84 | + { | |
85 | + title:'用户账号', | |
86 | + align:"center", | |
87 | + dataIndex: 'username' | |
88 | + },{ | |
89 | + title:'用户姓名', | |
90 | + align:"center", | |
91 | + dataIndex: 'realname' | |
92 | + },{ | |
93 | + title: '头像', | |
94 | + align: "center", | |
95 | + width: 120, | |
96 | + dataIndex: 'avatar', | |
97 | + scopedSlots: {customRender: "avatarslot"} | |
98 | + },{ | |
99 | + title:'生日', | |
100 | + align:"center", | |
101 | + dataIndex: 'birthday' | |
102 | + },{ | |
103 | + title: '性别', | |
104 | + align: "center", | |
105 | + dataIndex: 'sex', | |
106 | + customRender: (text) => { | |
107 | + //字典值翻译通用方法 | |
108 | + return filterDictTextByCache('sex', text); | |
109 | + } | |
110 | + },{ | |
111 | + title:'手机号', | |
112 | + align:"center", | |
113 | + dataIndex: 'phone' | |
114 | + },{ | |
115 | + title: '操作', | |
116 | + dataIndex: 'action', | |
117 | + scopedSlots: {customRender: 'action'}, | |
118 | + align: "center", | |
119 | + width: 170 | |
120 | + } | |
121 | + ], | |
122 | + url: { | |
123 | + list: "/sys/online/list" | |
124 | + }, | |
125 | + dictOptions:{}, | |
126 | + } | |
127 | + }, | |
128 | + created() { | |
129 | + }, | |
130 | + computed: { | |
131 | + importExcelUrl: function(){ | |
132 | + return `${window._CONFIG['domianURL']}/${this.url.importExcelUrl}`; | |
133 | + }, | |
134 | + }, | |
135 | + methods: { | |
136 | + getAvatarView: function (avatar) { | |
137 | + return getFileAccessHttpUrl(avatar) | |
138 | + }, | |
139 | + handleForce(record) { | |
140 | + let that = this; | |
141 | + let forceParam = { | |
142 | + token: record.token | |
143 | + } | |
144 | + return forceLogout(forceParam).then((res) => { | |
145 | + if (res.success) { | |
146 | + that.loadData(); | |
147 | + this.$message.success('强制退出用户”'+record.realname+'“成功!'); | |
148 | + } else { | |
149 | + that.$message.warning(res.message); | |
150 | + } | |
151 | + }) | |
152 | + } | |
153 | + } | |
154 | + } | |
155 | +</script> | |
156 | +<style scoped> | |
157 | + @import '~@assets/less/common.less'; | |
158 | +</style> | |
0 | 159 | \ No newline at end of file |
... | ... |
jeecg-boot/db/jeecgboot-mysql-5.7.sql
... | ... | @@ -5687,6 +5687,7 @@ INSERT INTO `sys_permission` VALUES ('fb367426764077dcf94640c843733985', '2a470f |
5687 | 5687 | INSERT INTO `sys_permission` VALUES ('fba41089766888023411a978d13c0aa4', 'e41b69c57a941a3bbcce45032fe57605', 'AUTO树表单列表', '/online/cgformTreeList/:code', 'modules/online/cgform/auto/OnlCgformTreeList', NULL, NULL, 1, NULL, '1', 9.00, 0, NULL, 1, 1, NULL, 1, NULL, 'admin', '2019-05-21 14:46:50', 'admin', '2019-06-11 13:52:52', 0, 0, '1', NULL); |
5688 | 5688 | INSERT INTO `sys_permission` VALUES ('fc810a2267dd183e4ef7c71cc60f4670', '700b7f95165c46cc7a78bf227aa8fed3', '请求追踪', '/monitor/HttpTrace', 'modules/monitor/HttpTrace', NULL, NULL, 1, NULL, NULL, 4.00, 0, NULL, 1, 1, NULL, 0, NULL, 'admin', '2019-04-02 09:46:19', 'admin', '2019-04-02 11:37:27', 0, 0, NULL, NULL); |
5689 | 5689 | INSERT INTO `sys_permission` VALUES ('fedfbf4420536cacc0218557d263dfea', '6e73eb3c26099c191bf03852ee1310a1', '新消息通知', '/account/settings/notification', 'account/settings/Notification', NULL, NULL, 1, 'NotificationSettings', NULL, NULL, NULL, '', 1, 1, NULL, NULL, NULL, NULL, '2018-12-26 19:02:05', NULL, NULL, 0, 0, NULL, NULL); |
5690 | +INSERT INTO `sys_permission` VALUES ('1402436404646010882', '08e6b9dc3c04489c8e1ff2ce6f105aa4', '在线用户', '/isystem/online', 'system/SysOnlineList', NULL, NULL, 1, NULL, '1', 1.00, 0, NULL, 1, 1, 0, 0, NULL, 'admin', '2021-06-09 09:24:30', 'admin', '2021-06-09 09:37:20', 0, 0, '1', 0); | |
5690 | 5691 | |
5691 | 5692 | -- ---------------------------- |
5692 | 5693 | -- Table structure for sys_permission_data_rule |
... | ... |
jeecg-boot/db/增量SQL/2.4.3升级到2.4.5增量MYSQL.sql
... | ... | @@ -13,6 +13,8 @@ ADD COLUMN `third_user_id` varchar(100) NULL COMMENT '第三方app用户账号' |
13 | 13 | |
14 | 14 | -- 新增第三方APP消息测试菜单 |
15 | 15 | INSERT INTO `sys_permission` (`id`, `parent_id`, `name`, `url`, `component`, `is_route`, `component_name`, `redirect`, `menu_type`, `perms`, `perms_type`, `sort_no`, `always_show`, `icon`, `is_leaf`, `keep_alive`, `hidden`, `description`, `create_by`, `create_time`, `update_by`, `update_time`, `del_flag`, `rule_flag`, `status`, `internal_or_external`) VALUES ('1387612436586065922', '2a470fc0c3954d9dbb61de6d80846549', '第三方APP消息测试', '/jeecg/ThirdAppMessageTest', 'jeecg/ThirdAppMessageTest', '1', NULL, NULL, '1', NULL, '1', '3', '0', NULL, '1', '0', '0', NULL, 'admin', '2021-04-29 11:39:20', 'admin', '2021-04-29 11:39:27', '0', '0', '1', '0'); |
16 | +-- 新增监控在线用户 | |
17 | +INSERT INTO `sys_permission` (`id`, `parent_id`, `name`, `url`, `component`, `is_route`, `component_name`, `redirect`, `menu_type`, `perms`, `perms_type`, `sort_no`, `always_show`, `icon`, `is_leaf`, `keep_alive`, `hidden`, `description`, `create_by`, `create_time`, `update_by`, `update_time`, `del_flag`, `rule_flag`, `status`, `internal_or_external`) VALUES ('1402436404646010882', '08e6b9dc3c04489c8e1ff2ce6f105aa4', '在线用户', '/isystem/online', 'system/SysOnlineList', NULL, NULL, 1, NULL, '1', 1.00, 0, NULL, 1, 1, 0, 0, NULL, 'admin', '2021-06-09 09:24:30', 'admin', '2021-06-09 09:37:20', 0, 0, '1', 0); | |
16 | 18 | |
17 | 19 | -- 定时任务:一个类允许配置多个调度 |
18 | 20 | -- 删除定时任务表唯一索引 |
... | ... |
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/DruidConfig.java
0 → 100644
1 | +package org.jeecg.config; | |
2 | + | |
3 | +import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure; | |
4 | +import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties; | |
5 | +import com.alibaba.druid.util.Utils; | |
6 | +import org.springframework.boot.autoconfigure.AutoConfigureAfter; | |
7 | +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | |
8 | +import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; | |
9 | +import org.springframework.boot.web.servlet.FilterRegistrationBean; | |
10 | +import org.springframework.context.annotation.Bean; | |
11 | +import org.springframework.context.annotation.Configuration; | |
12 | + | |
13 | +import javax.servlet.*; | |
14 | +import java.io.IOException; | |
15 | + | |
16 | +@Configuration | |
17 | +@AutoConfigureAfter(DruidDataSourceAutoConfigure.class) | |
18 | +public class DruidConfig { | |
19 | + | |
20 | + /** | |
21 | + * 带有广告的common.js全路径,druid-1.1.14 | |
22 | + */ | |
23 | + private static final String FILE_PATH = "support/http/resources/js/common.js"; | |
24 | + /** | |
25 | + * 原始脚本,触发构建广告的语句 | |
26 | + */ | |
27 | + private static final String ORIGIN_JS = "this.buildFooter();"; | |
28 | + /** | |
29 | + * 替换后的脚本 | |
30 | + */ | |
31 | + private static final String NEW_JS = "//this.buildFooter();"; | |
32 | + | |
33 | + /** | |
34 | + * 去除Druid监控页面的广告 | |
35 | + * | |
36 | + * @param properties DruidStatProperties属性集合 | |
37 | + * @return {@link org.springframework.boot.web.servlet.FilterRegistrationBean} | |
38 | + */ | |
39 | + @Bean | |
40 | + @ConditionalOnWebApplication | |
41 | + @ConditionalOnProperty(name = "spring.datasource.druid.stat-view-servlet.enabled", havingValue = "true") | |
42 | + public FilterRegistrationBean<RemoveAdFilter> removeDruidAdFilter( | |
43 | + DruidStatProperties properties) throws IOException { | |
44 | + // 获取web监控页面的参数 | |
45 | + DruidStatProperties.StatViewServlet config = properties.getStatViewServlet(); | |
46 | + // 提取common.js的配置路径 | |
47 | + String pattern = config.getUrlPattern() != null ? config.getUrlPattern() : "/druid/*"; | |
48 | + String commonJsPattern = pattern.replaceAll("\\*", "js/common.js"); | |
49 | + // 获取common.js | |
50 | + String text = Utils.readFromResource(FILE_PATH); | |
51 | + // 屏蔽 this.buildFooter(); 不构建广告 | |
52 | + final String newJs = text.replace(ORIGIN_JS, NEW_JS); | |
53 | + FilterRegistrationBean<RemoveAdFilter> registration = new FilterRegistrationBean<>(); | |
54 | + registration.setFilter(new RemoveAdFilter(newJs)); | |
55 | + registration.addUrlPatterns(commonJsPattern); | |
56 | + return registration; | |
57 | + } | |
58 | + | |
59 | + /** | |
60 | + * 删除druid的广告过滤器 | |
61 | + * | |
62 | + * @author BBF | |
63 | + */ | |
64 | + private class RemoveAdFilter implements Filter { | |
65 | + | |
66 | + private final String newJs; | |
67 | + | |
68 | + public RemoveAdFilter(String newJS) { | |
69 | + this.newJs = newJS; | |
70 | + } | |
71 | + | |
72 | + @Override | |
73 | + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) | |
74 | + throws IOException, ServletException { | |
75 | + chain.doFilter(request, response); | |
76 | + // 重置缓冲区,响应头不会被重置 | |
77 | + response.resetBuffer(); | |
78 | + response.getWriter().write(newJs); | |
79 | + } | |
80 | + } | |
81 | +} | |
... | ... |
jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/controller/SysOnlineController.java
0 → 100644
1 | +package org.jeecg.modules.system.controller; | |
2 | + | |
3 | +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; | |
4 | +import lombok.extern.slf4j.Slf4j; | |
5 | +import org.apache.commons.lang.StringUtils; | |
6 | +import org.apache.shiro.SecurityUtils; | |
7 | +import org.jeecg.common.api.vo.Result; | |
8 | +import org.jeecg.common.constant.CacheConstant; | |
9 | +import org.jeecg.common.constant.CommonConstant; | |
10 | +import org.jeecg.common.system.api.ISysBaseAPI; | |
11 | +import org.jeecg.common.system.util.JwtUtil; | |
12 | +import org.jeecg.common.system.vo.LoginUser; | |
13 | +import org.jeecg.common.util.RedisUtil; | |
14 | +import org.jeecg.common.util.oConvertUtils; | |
15 | +import org.jeecg.modules.base.service.BaseCommonService; | |
16 | +import org.jeecg.modules.system.service.ISysUserService; | |
17 | +import org.jeecg.modules.system.vo.SysOnlineVO; | |
18 | +import org.springframework.beans.BeanUtils; | |
19 | +import org.springframework.beans.factory.annotation.Autowired; | |
20 | +import org.springframework.data.redis.core.RedisTemplate; | |
21 | +import org.springframework.web.bind.annotation.*; | |
22 | + | |
23 | +import javax.annotation.Resource; | |
24 | +import java.util.ArrayList; | |
25 | +import java.util.Collection; | |
26 | +import java.util.Collections; | |
27 | +import java.util.List; | |
28 | + | |
29 | +/** | |
30 | + * @Description: 在线用户 | |
31 | + * @Author: chenli | |
32 | + * @Date: 2020-06-07 | |
33 | + * @Version: V1.0 | |
34 | + */ | |
35 | +@RestController | |
36 | +@RequestMapping("/sys/online") | |
37 | +@Slf4j | |
38 | +public class SysOnlineController { | |
39 | + | |
40 | + @Autowired | |
41 | + private RedisUtil redisUtil; | |
42 | + | |
43 | + @Autowired | |
44 | + public RedisTemplate redisTemplate; | |
45 | + | |
46 | + @Autowired | |
47 | + public ISysUserService userService; | |
48 | + | |
49 | + @Autowired | |
50 | + private ISysBaseAPI sysBaseAPI; | |
51 | + | |
52 | + @Resource | |
53 | + private BaseCommonService baseCommonService; | |
54 | + | |
55 | + @RequestMapping(value = "/list", method = RequestMethod.GET) | |
56 | + public Result<Page<SysOnlineVO>> list(@RequestParam(name="username", required=false) String username, @RequestParam(name="pageNo", defaultValue="1") Integer pageNo, | |
57 | + @RequestParam(name="pageSize", defaultValue="10") Integer pageSize) { | |
58 | + Collection<String> keys = redisTemplate.keys(CommonConstant.PREFIX_USER_TOKEN + "*"); | |
59 | + SysOnlineVO online; | |
60 | + List<SysOnlineVO> onlineList = new ArrayList<SysOnlineVO>(); | |
61 | + for (String key : keys) { | |
62 | + online = new SysOnlineVO(); | |
63 | + String token = (String) redisUtil.get(key); | |
64 | + if (!StringUtils.isEmpty(token)){ | |
65 | + online.setToken(token); | |
66 | + LoginUser loginUser = sysBaseAPI.getUserByName(JwtUtil.getUsername(token)); | |
67 | + BeanUtils.copyProperties(loginUser, online); | |
68 | + if (StringUtils.isNotEmpty(username)) { | |
69 | + if (StringUtils.equals(username, online.getUsername())) { | |
70 | + onlineList.add(online); | |
71 | + } | |
72 | + } else { | |
73 | + onlineList.add(online); | |
74 | + } | |
75 | + } | |
76 | + } | |
77 | + | |
78 | + Page<SysOnlineVO> page = new Page<SysOnlineVO>(pageNo, pageSize); | |
79 | + int count = onlineList.size(); | |
80 | + List<SysOnlineVO> pages = new ArrayList<>(); | |
81 | + //计算当前页第一条数据的下标 | |
82 | + int currId = pageNo>1 ? (pageNo-1)*pageSize:0; | |
83 | + for (int i=0; i<pageSize && i<count - currId;i++){ | |
84 | + pages.add(onlineList.get(currId+i)); | |
85 | + } | |
86 | + page.setSize(pageSize); | |
87 | + page.setCurrent(pageNo); | |
88 | + page.setTotal(count); | |
89 | + //计算分页总页数 | |
90 | + page.setPages(count %10 == 0 ? count/10 :count/10+1); | |
91 | + page.setRecords(pages); | |
92 | + | |
93 | + Collections.reverse(onlineList); | |
94 | + onlineList.removeAll(Collections.singleton(null)); | |
95 | + Result<Page<SysOnlineVO>> result = new Result<Page<SysOnlineVO>>(); | |
96 | + result.setSuccess(true); | |
97 | + result.setResult(page); | |
98 | + return result; | |
99 | + } | |
100 | + | |
101 | + /** | |
102 | + * 强退用户 | |
103 | + */ | |
104 | + @RequestMapping(value = "/forceLogout",method = RequestMethod.POST) | |
105 | + public Result<Object> forceLogout(@RequestBody SysOnlineVO online) { | |
106 | + //用户退出逻辑 | |
107 | + if(oConvertUtils.isEmpty(online.getToken())) { | |
108 | + return Result.error("退出登录失败!"); | |
109 | + } | |
110 | + String username = JwtUtil.getUsername(online.getToken()); | |
111 | + LoginUser sysUser = sysBaseAPI.getUserByName(username); | |
112 | + if(sysUser!=null) { | |
113 | + baseCommonService.addLog("强制: "+sysUser.getRealname()+"退出成功!", CommonConstant.LOG_TYPE_1, null,sysUser); | |
114 | + log.info(" 强制 "+sysUser.getRealname()+"退出成功! "); | |
115 | + //清空用户登录Token缓存 | |
116 | + redisUtil.del(CommonConstant.PREFIX_USER_TOKEN + online.getToken()); | |
117 | + //清空用户登录Shiro权限缓存 | |
118 | + redisUtil.del(CommonConstant.PREFIX_USER_SHIRO_CACHE + sysUser.getId()); | |
119 | + //清空用户的缓存信息(包括部门信息),例如sys:cache:user::<username> | |
120 | + redisUtil.del(String.format("%s::%s", CacheConstant.SYS_USERS_CACHE, sysUser.getUsername())); | |
121 | + //调用shiro的logout | |
122 | + SecurityUtils.getSubject().logout(); | |
123 | + return Result.ok("退出登录成功!"); | |
124 | + }else { | |
125 | + return Result.error("Token无效!"); | |
126 | + } | |
127 | + } | |
128 | +} | |
... | ... |
jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/vo/SysOnlineVO.java
0 → 100644
1 | +package org.jeecg.modules.system.vo; | |
2 | + | |
3 | +import com.fasterxml.jackson.annotation.JsonFormat; | |
4 | +import lombok.Data; | |
5 | +import org.jeecg.common.aspect.annotation.Dict; | |
6 | +import org.springframework.format.annotation.DateTimeFormat; | |
7 | + | |
8 | +import java.util.Date; | |
9 | + | |
10 | +/** | |
11 | + * | |
12 | + * @Author: chenli | |
13 | + * @Date: 2020-06-07 | |
14 | + * @Version: V1.0 | |
15 | + */ | |
16 | +@Data | |
17 | +public class SysOnlineVO { | |
18 | + /** | |
19 | + * 会话id | |
20 | + */ | |
21 | + private String id; | |
22 | + | |
23 | + /** | |
24 | + * 会话编号 | |
25 | + */ | |
26 | + private String token; | |
27 | + | |
28 | + /** | |
29 | + * 用户名 | |
30 | + */ | |
31 | + private String username; | |
32 | + | |
33 | + /** | |
34 | + * 用户名 | |
35 | + */ | |
36 | + private String realname; | |
37 | + | |
38 | + /** | |
39 | + * 头像 | |
40 | + */ | |
41 | + private String avatar; | |
42 | + | |
43 | + /** | |
44 | + * 生日 | |
45 | + */ | |
46 | + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd") | |
47 | + @DateTimeFormat(pattern = "yyyy-MM-dd") | |
48 | + private Date birthday; | |
49 | + | |
50 | + /** | |
51 | + * 性别(1:男 2:女) | |
52 | + */ | |
53 | + @Dict(dicCode = "sex") | |
54 | + private Integer sex; | |
55 | + | |
56 | + /** | |
57 | + * 手机号 | |
58 | + */ | |
59 | + private String phone; | |
60 | +} | |
... | ... |