8 changed files with 391 additions and 10 deletions
-
9src/src/main/java/com/my/graphiteDigesterBg/diframe/ActiveRecordField.java
-
179src/src/main/java/com/my/graphiteDigesterBg/diframe/DiActiveRecord.java
-
10src/src/main/java/com/my/graphiteDigesterBg/diframe/DiActiveRecordCriteria.java
-
15src/src/main/java/com/my/graphiteDigesterBg/diframe/DiApiControllerBase.java
-
95src/src/main/java/com/my/graphiteDigesterBg/diframe/api/DiApiUser.java
-
40src/src/main/java/com/my/graphiteDigesterBg/diframe/mapper/DiActiveRecordMapper.java
-
49src/src/main/java/com/my/graphiteDigesterBg/diframe/model/DiMdbUser.java
-
4src/src/main/resources/application.yml
@ -0,0 +1,9 @@ |
|||||
|
package com.my.graphiteDigesterBg.diframe; |
||||
|
import java.lang.annotation.ElementType; |
||||
|
import java.lang.annotation.Retention; |
||||
|
import java.lang.annotation.RetentionPolicy; |
||||
|
import java.lang.annotation.Target; |
||||
|
@Retention(RetentionPolicy.RUNTIME) |
||||
|
@Target({ElementType.FIELD}) |
||||
|
public @interface ActiveRecordField { |
||||
|
} |
@ -1,12 +1,181 @@ |
|||||
package com.my.graphiteDigesterBg.diframe; |
package com.my.graphiteDigesterBg.diframe; |
||||
|
import com.fasterxml.jackson.annotation.JsonIgnore; |
||||
import com.my.graphiteDigesterBg.diframe.mapper.DiActiveRecordMapper; |
import com.my.graphiteDigesterBg.diframe.mapper.DiActiveRecordMapper; |
||||
abstract public class DiActiveRecord { |
|
||||
|
import java.lang.reflect.Constructor; |
||||
|
import java.lang.reflect.Field; |
||||
|
import java.lang.reflect.InvocationTargetException; |
||||
|
import java.lang.reflect.Method; |
||||
|
import java.util.ArrayList; |
||||
|
import java.util.HashMap; |
||||
|
import java.util.List; |
||||
|
import java.util.Map; |
||||
|
public class DiActiveRecord { |
||||
|
// is new record |
||||
|
@JsonIgnore |
||||
|
public Boolean isNewRecord = true; |
||||
|
|
||||
|
// save model |
||||
|
public void save() { |
||||
|
var context = DiApplicationContextProvider.getContext(); |
||||
|
DiActiveRecordMapper mapper = context.getBean(DiActiveRecordMapper.class); |
||||
|
String tableName = DiActiveRecord.getTableNameFromModelClass(this.getClass()); |
||||
|
Map<String,Object> data = DiActiveRecord.exportModelData(this); |
||||
|
if ( this.isNewRecord ) { |
||||
|
mapper.insert(tableName, data); |
||||
|
} else { |
||||
|
mapper.update(tableName, (Integer)data.get("id"), data); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// delete model |
||||
|
public void delete() { |
||||
|
var context = DiApplicationContextProvider.getContext(); |
||||
|
DiActiveRecordMapper mapper = context.getBean(DiActiveRecordMapper.class); |
||||
|
String tableName = DiActiveRecord.getTableNameFromModelClass(this.getClass()); |
||||
|
Map<String,Object> data = DiActiveRecord.exportModelData(this); |
||||
|
mapper.delete(tableName, (Integer)data.get("id")); |
||||
|
} |
||||
|
|
||||
|
// set attributes |
||||
|
public void setAttributes( Map<String,Object> attributes ) { |
||||
|
for ( Map.Entry<String,Object> entry : attributes.entrySet() ) { |
||||
|
String key = entry.getKey(); |
||||
|
Field field = null; |
||||
|
try { |
||||
|
field = this.getClass().getDeclaredField(key); |
||||
|
} catch (NoSuchFieldException e) { |
||||
|
continue ; |
||||
|
} |
||||
|
|
||||
|
Class<?> fieldType = field.getType(); |
||||
|
if ( !fieldType.isAssignableFrom(entry.getValue().getClass()) ) { |
||||
|
throw new RuntimeException("Attribute type mismatch: " + key); |
||||
|
} |
||||
|
|
||||
|
field.setAccessible(true); |
||||
|
try { |
||||
|
field.set(this, entry.getValue()); |
||||
|
} catch (IllegalAccessException e) { |
||||
|
throw new RuntimeException(e); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
// find by id |
// find by id |
||||
public static void findOne(Integer id) { |
|
||||
|
public static <T extends DiActiveRecord> T findOne( Class<T> modelClass, Integer id) { |
||||
|
return DiActiveRecord.findOne(modelClass, Map.of("id", id)); |
||||
|
} |
||||
|
|
||||
|
// find one by conditions |
||||
|
public static <T extends DiActiveRecord> T findOne( Class<T> modelClass, Map<String,Object> conditions) { |
||||
|
var criteria = new DiActiveRecordCriteria(); |
||||
|
criteria.tableName = DiActiveRecord.getTableNameFromModelClass(modelClass); |
||||
|
criteria.conditions = conditions; |
||||
|
criteria.limit = 1; |
||||
|
|
||||
|
var context = DiApplicationContextProvider.getContext(); |
||||
|
DiActiveRecordMapper mapper = context.getBean(DiActiveRecordMapper.class); |
||||
|
var rows = mapper.find(criteria); |
||||
|
if (rows.isEmpty()) { |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
T model = DiActiveRecord.fillModel(modelClass, rows.get(0)); |
||||
|
model.isNewRecord = false; |
||||
|
return model; |
||||
|
} |
||||
|
|
||||
|
// find all by criteria |
||||
|
public static <T extends DiActiveRecord> List<T> find( Class<T> modelClass, DiActiveRecordCriteria criteria ) { |
||||
|
criteria.tableName = DiActiveRecord.getTableNameFromModelClass(modelClass); |
||||
var context = DiApplicationContextProvider.getContext(); |
var context = DiApplicationContextProvider.getContext(); |
||||
DiActiveRecordMapper mapper = context.getBean(DiActiveRecordMapper.class); |
DiActiveRecordMapper mapper = context.getBean(DiActiveRecordMapper.class); |
||||
var row = mapper.findById("di_app_users", id); |
|
||||
// // @TODO : 这里要根据配置参数计算 ~~~ |
|
||||
System.out.println("DiActiveRecord.findById"); |
|
||||
|
var rows = mapper.find(criteria); |
||||
|
|
||||
|
List<T> models = new ArrayList<>(); |
||||
|
for ( var row : rows ) { |
||||
|
T model = DiActiveRecord.fillModel(modelClass, row); |
||||
|
model.isNewRecord = false; |
||||
|
models.add(model); |
||||
|
} |
||||
|
return models; |
||||
|
} |
||||
|
|
||||
|
// get table name from model class |
||||
|
private static <T> String getTableNameFromModelClass( Class<T> modelClass) { |
||||
|
Method tableNameGetter = null; |
||||
|
try { |
||||
|
tableNameGetter = modelClass.getMethod("getTableName"); |
||||
|
} catch (NoSuchMethodException e) { |
||||
|
throw new RuntimeException(e); |
||||
|
} |
||||
|
String tableName = null; |
||||
|
try { |
||||
|
tableName = (String)tableNameGetter.invoke(null); |
||||
|
} catch (IllegalAccessException | InvocationTargetException e) { |
||||
|
throw new RuntimeException(e); |
||||
|
} |
||||
|
return tableName; |
||||
|
} |
||||
|
|
||||
|
// fill model |
||||
|
private static <T> T fillModel(Class<T> modelClass, Map<String,Object> data) { |
||||
|
Constructor<?> modelConstructor = null; |
||||
|
try { |
||||
|
modelConstructor = modelClass.getDeclaredConstructor(); |
||||
|
} catch (NoSuchMethodException e) { |
||||
|
throw new RuntimeException(e); |
||||
|
} |
||||
|
|
||||
|
T model = null; |
||||
|
try { |
||||
|
model = (T)modelConstructor.newInstance(); |
||||
|
} catch (InvocationTargetException | InstantiationException | IllegalAccessException e) { |
||||
|
throw new RuntimeException(e); |
||||
|
} |
||||
|
|
||||
|
for ( Map.Entry<String,Object> entry : data.entrySet() ) { |
||||
|
String key = entry.getKey(); |
||||
|
Field field = null; |
||||
|
try { |
||||
|
field = model.getClass().getDeclaredField(key); |
||||
|
} catch (NoSuchFieldException e) { |
||||
|
continue ; |
||||
|
} |
||||
|
|
||||
|
Class<?> fieldType = field.getType(); |
||||
|
if ( !fieldType.isAssignableFrom(entry.getValue().getClass()) ) { |
||||
|
throw new RuntimeException("Attribute type mismatch: " + key); |
||||
|
} |
||||
|
|
||||
|
field.setAccessible(true); |
||||
|
try { |
||||
|
field.set(model, entry.getValue()); |
||||
|
} catch (IllegalAccessException e) { |
||||
|
throw new RuntimeException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return model; |
||||
|
} |
||||
|
|
||||
|
// model to map |
||||
|
private static Map<String,Object> exportModelData(Object model) { |
||||
|
Map<String,Object> data = new HashMap<>(); |
||||
|
var fields = model.getClass().getDeclaredFields(); |
||||
|
for ( var field : fields ) { |
||||
|
var annotation = field.getAnnotation(ActiveRecordField.class); |
||||
|
if ( annotation == null ) { |
||||
|
continue ; |
||||
|
} |
||||
|
|
||||
|
field.setAccessible(true); |
||||
|
try { |
||||
|
data.put(field.getName(), field.get(model)); |
||||
|
} catch (IllegalAccessException e) { |
||||
|
throw new RuntimeException(e); |
||||
|
} |
||||
|
} |
||||
|
return data; |
||||
} |
} |
||||
} |
} |
@ -0,0 +1,10 @@ |
|||||
|
package com.my.graphiteDigesterBg.diframe; |
||||
|
import java.util.Map; |
||||
|
public class DiActiveRecordCriteria { |
||||
|
// table name |
||||
|
public String tableName; |
||||
|
// conditions |
||||
|
public Map<String,Object> conditions; |
||||
|
// limit |
||||
|
public Integer limit; |
||||
|
} |
@ -1,18 +1,107 @@ |
|||||
package com.my.graphiteDigesterBg.diframe.api; |
package com.my.graphiteDigesterBg.diframe.api; |
||||
|
import com.my.graphiteDigesterBg.diframe.DiActiveRecord; |
||||
|
import com.my.graphiteDigesterBg.diframe.DiActiveRecordCriteria; |
||||
import com.my.graphiteDigesterBg.diframe.DiApiControllerBase; |
import com.my.graphiteDigesterBg.diframe.DiApiControllerBase; |
||||
import com.my.graphiteDigesterBg.diframe.DiApiResponse; |
import com.my.graphiteDigesterBg.diframe.DiApiResponse; |
||||
import com.my.graphiteDigesterBg.diframe.model.DiMdbUser; |
import com.my.graphiteDigesterBg.diframe.model.DiMdbUser; |
||||
|
import jakarta.servlet.http.HttpServletRequest; |
||||
import org.springframework.stereotype.Controller; |
import org.springframework.stereotype.Controller; |
||||
|
import org.springframework.web.bind.annotation.PostMapping; |
||||
|
import org.springframework.web.bind.annotation.RequestBody; |
||||
import org.springframework.web.bind.annotation.RequestMapping; |
import org.springframework.web.bind.annotation.RequestMapping; |
||||
import org.springframework.web.bind.annotation.ResponseBody; |
import org.springframework.web.bind.annotation.ResponseBody; |
||||
|
import java.util.Map; |
||||
|
import java.util.UUID; |
||||
@Controller |
@Controller |
||||
public class DiApiUser extends DiApiControllerBase { |
public class DiApiUser extends DiApiControllerBase { |
||||
@ResponseBody |
@ResponseBody |
||||
@RequestMapping("/api/user/login") |
@RequestMapping("/api/user/login") |
||||
public DiApiResponse login() { |
|
||||
DiMdbUser.findOne(1); |
|
||||
|
public DiApiResponse login(@RequestBody Map<String,Object> params) { |
||||
|
Integer id = (Integer)params.get("id"); |
||||
|
String password = (String)params.get("password"); |
||||
|
|
||||
|
var user = DiActiveRecord.findOne(DiMdbUser.class, id); |
||||
|
if ( null == user || !user.matchPassword(password) ) { |
||||
|
return this.error("无效的账号或密码"); |
||||
|
} |
||||
|
|
||||
|
user.accessToken = UUID.randomUUID().toString(); |
||||
|
user.accessTokenExpiredAt = (int)(System.currentTimeMillis() / 1000) + 3600 * 24; |
||||
|
user.save(); |
||||
|
return this.success(Map.of( |
||||
|
"accessToken", user.accessToken, |
||||
|
"accessTokenExpiredAt", user.accessTokenExpiredAt |
||||
|
)); |
||||
|
} |
||||
|
|
||||
|
@ResponseBody |
||||
|
@RequestMapping("/api/user/logout") |
||||
|
public DiApiResponse logout( HttpServletRequest request ) { |
||||
|
String accessToken = request.getHeader("App-Access-Token"); |
||||
|
var user = DiActiveRecord.findOne(DiMdbUser.class, Map.of("accessToken", accessToken)); |
||||
|
if ( null == user ) { |
||||
|
return this.success(); |
||||
|
} |
||||
|
|
||||
|
user.accessToken = ""; |
||||
|
user.accessTokenExpiredAt = 0; |
||||
|
user.save(); |
||||
|
return this.success(); |
||||
|
} |
||||
|
|
||||
|
@ResponseBody |
||||
|
@PostMapping("/api/user/save") |
||||
|
public DiApiResponse save( HttpServletRequest request, @RequestBody Map<String,Object> params ) { |
||||
|
DiMdbUser curUser = this.getUserFromRequest(request); |
||||
|
Integer id = (Integer)params.get("id"); |
||||
|
Map<String,Object> data = (Map<String,Object>)params.get("data"); |
||||
|
|
||||
|
var user = new DiMdbUser(); |
||||
|
user.salt = UUID.randomUUID().toString().substring(0, 8); |
||||
|
user.createdAt = (int)(System.currentTimeMillis() / 1000); |
||||
|
user.createdBy = curUser.id; |
||||
|
if ( null != id ) { |
||||
|
user = DiActiveRecord.findOne(DiMdbUser.class, id); |
||||
|
} |
||||
|
|
||||
|
user.setAttributes(data); |
||||
|
user.save(); |
||||
return this.success(); |
return this.success(); |
||||
} |
} |
||||
|
|
||||
public void logout() {} |
|
||||
|
@ResponseBody |
||||
|
@PostMapping("/api/user/delete") |
||||
|
public DiApiResponse delete( @RequestBody Map<String,Object> params ) { |
||||
|
Integer id = (Integer)params.get("id"); |
||||
|
var user = DiActiveRecord.findOne(DiMdbUser.class, id); |
||||
|
if ( null == user ) { |
||||
|
return this.success(); |
||||
|
} |
||||
|
|
||||
|
user.delete(); |
||||
|
return this.success(); |
||||
|
} |
||||
|
|
||||
|
@ResponseBody |
||||
|
@PostMapping("/api/user/updatePassword") |
||||
|
public DiApiResponse updatePassword( @RequestBody Map<String,Object> params ) { |
||||
|
Integer id = (Integer)params.get("id"); |
||||
|
String password = (String)params.get("password"); |
||||
|
var user = DiActiveRecord.findOne(DiMdbUser.class, id); |
||||
|
if ( null == user ) { |
||||
|
return this.error("无效的用户"); |
||||
|
} |
||||
|
user.password = user.hashPassword(password); |
||||
|
user.save(); |
||||
|
return this.success(); |
||||
|
} |
||||
|
|
||||
|
@ResponseBody |
||||
|
@RequestMapping("/api/user/list") |
||||
|
public DiApiResponse list() { |
||||
|
var criteria = new DiActiveRecordCriteria(); |
||||
|
criteria.limit = 10; |
||||
|
var users = DiActiveRecord.find(DiMdbUser.class, criteria); |
||||
|
return this.success(Map.of("list",users)); |
||||
|
} |
||||
} |
} |
@ -1,9 +1,45 @@ |
|||||
package com.my.graphiteDigesterBg.diframe.mapper; |
package com.my.graphiteDigesterBg.diframe.mapper; |
||||
import org.apache.ibatis.annotations.Mapper; |
|
||||
import org.apache.ibatis.annotations.Select; |
|
||||
|
import com.my.graphiteDigesterBg.diframe.DiActiveRecordCriteria; |
||||
|
import org.apache.ibatis.annotations.*; |
||||
|
|
||||
|
import java.util.List; |
||||
import java.util.Map; |
import java.util.Map; |
||||
@Mapper |
@Mapper |
||||
public interface DiActiveRecordMapper { |
public interface DiActiveRecordMapper { |
||||
|
@Select( |
||||
|
"<script>" + |
||||
|
"SELECT * FROM ${tableName} " + |
||||
|
"<if test='conditions != null'>" + |
||||
|
"WHERE " + |
||||
|
"<foreach collection='conditions' item='value' index='key' separator=' AND '>${key} = #{value}</foreach>" + |
||||
|
"</if>" + |
||||
|
"LIMIT #{limit}" + |
||||
|
"</script>" |
||||
|
) |
||||
|
List<Map<String,Object>> find(DiActiveRecordCriteria criteria); |
||||
|
|
||||
@Select("SELECT * FROM ${tableName} WHERE id = #{id}") |
@Select("SELECT * FROM ${tableName} WHERE id = #{id}") |
||||
Map<String,Object> findById(String tableName, Integer id); |
Map<String,Object> findById(String tableName, Integer id); |
||||
|
|
||||
|
@Insert( |
||||
|
"<script>" + |
||||
|
"INSERT INTO ${tableName} " + |
||||
|
"<foreach collection='data' item='value' index='key' open='(' separator=',' close=')'>${key}</foreach>" + |
||||
|
"VALUES " + |
||||
|
"<foreach collection='data' item='value' index='key' open='(' separator=',' close=')'>#{value}</foreach>" + |
||||
|
"</script>" |
||||
|
) |
||||
|
Integer insert(String tableName, Map<String,Object> data); |
||||
|
|
||||
|
@Update( |
||||
|
"<script>" + |
||||
|
"UPDATE ${tableName} SET " + |
||||
|
"<foreach collection='data' item='value' index='key' separator=','>${key} = #{value}</foreach>" + |
||||
|
"WHERE id = #{id}" + |
||||
|
"</script>" |
||||
|
) |
||||
|
Integer update(String tableName, Integer id, Map<String,Object> data); |
||||
|
|
||||
|
@Delete("DELETE FROM ${tableName} WHERE id = #{id}") |
||||
|
Integer delete(String tableName, Integer id); |
||||
} |
} |
@ -1,5 +1,54 @@ |
|||||
package com.my.graphiteDigesterBg.diframe.model; |
package com.my.graphiteDigesterBg.diframe.model; |
||||
|
import com.fasterxml.jackson.annotation.JsonIgnore; |
||||
|
import com.my.graphiteDigesterBg.diframe.ActiveRecordField; |
||||
import com.my.graphiteDigesterBg.diframe.DiActiveRecord; |
import com.my.graphiteDigesterBg.diframe.DiActiveRecord; |
||||
|
import org.springframework.util.DigestUtils; |
||||
public class DiMdbUser extends DiActiveRecord { |
public class DiMdbUser extends DiActiveRecord { |
||||
|
@ActiveRecordField |
||||
|
public Integer id; |
||||
|
|
||||
|
@ActiveRecordField |
||||
|
public String account; |
||||
|
|
||||
|
@JsonIgnore |
||||
|
@ActiveRecordField |
||||
|
public String password; |
||||
|
|
||||
|
@JsonIgnore |
||||
|
@ActiveRecordField |
||||
|
public String salt; |
||||
|
|
||||
|
@ActiveRecordField |
||||
|
public Integer roleId; |
||||
|
|
||||
|
@ActiveRecordField |
||||
|
public Integer createdAt; |
||||
|
|
||||
|
@ActiveRecordField |
||||
|
public Integer createdBy; |
||||
|
|
||||
|
@JsonIgnore |
||||
|
@ActiveRecordField |
||||
|
public String accessToken; |
||||
|
|
||||
|
@JsonIgnore |
||||
|
@ActiveRecordField |
||||
|
public Integer accessTokenExpiredAt; |
||||
|
|
||||
|
// get table name |
||||
|
public static String getTableName() { |
||||
|
return "app_users"; |
||||
|
} |
||||
|
|
||||
|
// check if password matches |
||||
|
public Boolean matchPassword(String password) { |
||||
|
String hash = this.hashPassword(password); |
||||
|
return this.password.equals(hash); |
||||
|
} |
||||
|
|
||||
|
// hash password |
||||
|
public String hashPassword(String password) { |
||||
|
String salt = this.salt; |
||||
|
return DigestUtils.md5DigestAsHex((salt + password + salt).getBytes()); |
||||
|
} |
||||
} |
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue